
Ops says RDP latency is fine. The session is connected. RD Connection Broker handed out a Session Host. The clerk is still waiting for the desktop to finish painting. That is the Microsoft RDS response-time problem in one sentence: protocol health is not the wait the user feels.
Direct answer: Measure RDS response time from what the user sees on the screen — on the glass — not only from RDP round-trip, network latency, host CPU, or broker health. Time the interval from a click or key until the expected screen state is visible. That interval is click-to-ready. It is the number a CAB and a help desk can share.
This post is about Microsoft Remote Desktop Services (RDS) — Session Hosts, RemoteApp, RD Gateway / RD Web — not Amazon Relational Database Service. The sibling method for walking a real RDS path under load is in How to load test Microsoft RDS. Tooling context lives on best RDS load testing tools. This article is narrower: how to time the wait itself.
Why RDP can look fine while users wait
RDP is a remoting protocol. It moves pixels out and keyboard and mouse in. Performance Monitor counters, RD Connection Broker health, and “session connected” status are good at telling you the channel is up. They are not a stopwatch for “the published app painted.”
A session can stream a logon animation, a spinning cursor, or a half-drawn shell and still look connected. Profile attach, GPO, FSLogix (or equivalent), and the line-of-business app itself sit after RDP is already up. RD Web is a website. Hammering it with an HTTP generator times the portal, not the Session Host farm.
RDP round-trip is useful. It is not the same as “the shell appeared.”
The same glass-versus-protocol split shows up on Citrix farms — see the sister post How to measure Citrix response time. Host CPU can sit at 40 percent while login-to-shell stretches and the RemoteApp paints late. Green session tiles do not close that ticket.

Left column is what protocol and host timers see. Right column is what the user waits on. Only the right column is RDS response time as a person experiences it.
Which steps to time
Write the steps before you pick a tool. Three intervals cover most RDS UX arguments:
- Logon-to-shell — from credentials submitted (or the RD Web / Gateway launch) until a usable desktop or RemoteApp window is ready: shell, icons, no logon animation. Not “the broker returned a session.”
- RemoteApp / published app launch — from the icon or Start-menu click until first paint of the business app: Epic, SAP, Outlook, the line-of-business client. Not “the process started on the Session Host.”
- In-session transactions — from the click or key that starts the work until the ready state: search, open the record, save, print, switch RemoteApps. This is the wait that generates “the desktop is slow” tickets after everyone is already in session.
Pin the image, Session Host SKU, client (mstsc / Remote Desktop app), RD Web or Gateway build, and the published collection. A timer on a moving image is a story, not a measurement.
For capacity work, run a logon storm separately from density. Storm stresses login-to-shell. Density stresses in-session transactions on hosts that already have users. Blending them hides which interval broke.
Time it like a script, not a feeling
On RDP there is no DOM event that means “done.” The practical signal is the bitmap. The measurement pattern is the same one CitraTest has used for years on remoted apps — including Citrix HDX farms — and it applies unchanged to Microsoft RDS:
- StartTimer when the user action happens — submit, click, or key.
- WaitForImage until a baseline of the ready state is visible: the shell, the RemoteApp window, the chart.
- StopTimer when that image matches.
The recorded interval is click-to-ready for that step. Image recognition — and OCR when the ready state is text — is how you know the glass painted. You do not install an agent on the Session Host to learn that. That is the core of on-the-glass APM.

That pipeline is the same for login, RemoteApp launch, and in-session work. Change the start action and the ready image. Keep the timer.
Stopwatch versus synthetic
A person with a stopwatch can time one logon on a quiet morning. That is a demo, not a measurement program.
A one-off cannot give you a tail. It cannot run at 8:00 every weekday. It cannot hold two hundred sessions and still time open-record. It cannot attach a screenshot of the failed state. Means hide the damage until the service desk is already taking calls.
A synthetic does the same StartTimer → WaitForImage → StopTimer path as a real Remote Desktop client, on a schedule or under load, with the same baseline images. That is on-the-glass monitoring when it runs continuously, and an RDS load test when it ramps concurrent users.
If you only have a stopwatch, use it to write the acceptance line — then automate the line. Example language teams already put on capacity plans (schematic only, not a customer dataset): logon-to-shell 45 seconds at p95 and 60 seconds at p99; RemoteApp open 8 seconds at p95; fewer than 1 percent of runs fail, disconnect, or hang.
Continuous watch versus load test
On-the-glass is an idea, not a product SKU. Two jobs share the path:
- CitraTest VU load-tests capacity. Freeze the image, walk the real RD Web / Gateway / mstsc path, run storm and sustain as separate shapes, stop at the written budget. That is the concurrent-user number a CAB can defend. See CitraTest VU load testing.
- CitraTest APM watches production. The same login → RemoteApp → in-session steps, every N minutes, with alerts and screenshots when glass time slips. That is the morning watch after go-live. See CitraTest APM.
The lab answers “how many good sessions fit on this Session Host SKU.” The watch answers “is today’s logon-to-shell and open-record still inside the SLA.” Do not use a load-test generator as your only production monitor, and do not treat a single synthetic as a density number.
No agents on the Session Hosts or brokers are required for that path. Each virtual user or monitor is another Remote Desktop session. If the generator desktop is pegged, you are measuring the lab, not the farm.
What this is not
- It is not a replacement for Performance Monitor, RD Connection Broker health, or Azure Virtual Desktop / Windows 365 host metrics. Keep those for the remoting and host path.
- It is not an HTTP soak of RD Web or Gateway. Keep that for the front door.
- It is not “we have full-stack APM, so RDS is covered.” Remoted GUIs are pixels, not traces.
- It is not a promise that last quarter’s number survives a new Session Host image, profile policy, or a move to AVD. Re-time the glass when the image changes.
Take the interval users already wait
Microsoft RDS response time, as a user experiences it, is click-to-ready on the glass. Time logon-to-shell, RemoteApp launch, and the in-session steps that generate tickets. Use a stopwatch only to write the line. Use a synthetic to keep the line: CitraTest VU under load, CitraTest APM every shift.
When you need that path without agents on the farm — real Remote Desktop client, image and OCR, StartTimer to StopTimer — schedule a demo. Product pages: CitraTest VU and CitraTest APM.