Design prompt: a real-time dashboard
A live dashboard is a snapshot plus numbered changes, drawn once per frame, with each tile showing how old it is.
Predict first
Before you read: predict
A proctor console shows one tile per candidate, with a status and a flag count. Its socket brings two kinds of message, { id, status } and { id, flag: 1 }; in a busy second several arrive between two screen frames, often more than one for the same candidate. Drawing a tile is the console's costly step. Proctors need every frame to show each tile's newest status and exact flag count, with as few tile draws as that allows.
Which message handler should the console use?
The 30-second version
Load a snapshot stamped with the feed's sequence number, then apply only numbered deltas above it; a gap or a long break means a fresh snapshot. Apply every message to tile state at once, but draw changed tiles once per animation frame. Hidden tabs get no frames: keep state, not queues. Grey a tile whose own source has gone quiet.
A live dashboard is a snapshot plus numbered changes, drawn once per frame, with each tile showing how old it is.
How it works
Step 1 of 14
In the kitchen
- the state picture, stamped with the last event it includes → the snapshot and its sequence number (step 1)
Analogy: An electricity grid control room's wall diagram and event log
At the start of a shift the operator takes the control system's full state picture, stamped "includes event 4,812", and works from the numbered events after it.
| In the control room | In the dashboard |
|---|---|
| the state picture, stamped with the last event it includes | the snapshot and its sequence number (step 1) |
| events numbered after the stamp, applied in order; earlier ones are already in the picture | changes above the snapshot's number (steps 2–4) |
| after a link outage, the system re-reads every value rather than replaying what it missed | resync with a fresh snapshot (step 5) |
| every trip goes into the log at once, while the wall diagram is repainted once per scan cycle | apply each message, draw once per frame (steps 7–8) |
| a substation's symbols flagged "telemetry stale" while the link to the others stays busy | freshness per source (steps 12–13) |
In real terms: a dashboard holds one state per tile, built from a snapshot and the numbered changes after it, and draws that state at most once a frame.
Breaks down when: the wall diagram is repainted every cycle whether or not anything changed; a dashboard draws only the tiles that changed, and nothing at all while its tab is hidden (steps 8, 10).
Contrasting case: a printed timetable needs no numbered changes: it is reprinted whole.
Examples, run for real
4.1 ExamHall EXH-2260 → MR !2560: an alert list that grew all afternoon
During a three-hour ECON-201 sitting, the proctor console's alert sidebar kept every flag alert it received, one row each. By the third hour scrolling it stuttered on the hall's old laptop. MR !2560 keeps the newest 200.
EXH-2260 every alert kept · after 3 h, 1 alert every 4 s: 2,700 alerts held · 2,700 rows on the page
MR !2560 newest 200 kept · after 3 h: 200 alerts held · 200 rows on the page · newest alert on top: yes
The alerts are a log, so the list needs a bound; the tiles hold state, which needs none (steps 6, 14).
4.2 Proof domain, e-commerce checkout: one frame request per status
A shop's checkout board asked for a frame in every status handler. Five payment methods' statuses were handled before one frame, in Chromium's own requestAnimationFrame.
checkout board · 5 statuses handled before one frame · requestAnimationFrame in every handler: drawAll ran 5 times in that frame
checkout board · 5 statuses handled before one frame · one request, guarded by a queued flag: drawAll ran once in that frame
Each request is its own callback, so the board drew five times for one frame; a queued flag makes it one (step 9).
4.3 The board for "Design a live dashboard" (worked, not executed)
| Typical question | Steps that answer it | Mechanism owner |
|---|---|---|
| "40 live tiles for proctors: what does the client hold?" | 1–4, 6 | this lesson; the channel: fsd-realtime-transport |
| "Updates arrive 8 times a second. How do you render without jank?" | 7–10 | this lesson; interaction phases: perf-inp |
| "The laptop sleeps for 10 minutes. What does the dashboard show on wake?" | 5, 12–13 | this lesson; countdown clocks: fsd-clock-sync-timers |
Try it
Answer first, then check. Nothing is saved.
Question 1 of 5
A shop's checkout board shows how many payments each method has had declined today. GET /snapshot answers { seq, counts }, where seq is the number of the last decline the counts include. The socket sends each new decline as { seq, method }, numbered one by one, to the sockets open when it happens; the feed keeps no history. Declines keep happening while the board loads.
Which load sequence shows exact counts, whatever moment each decline happens at?
Question 2 of 5
On the proctor console, each candidate's laptop sends { id, status } every 5 s, and the feed server sends { type: 'heartbeat' } on the same socket every 5 s; a one-second timer re-applies the console's stale rule to every tile. The console loaded its snapshot at 14:19:00. Candidate c-7's laptop lost power just after its 14:20:00 status, and her tile went on showing "writing" until a proctor walked over. Candidate c-3 has been writing all session, so every status she sends says "writing"; c-9 moves to a new question every 10 s.
Which rule greys c-7's tile by 14:20:20 and never greys another tile?
Question 3 of 5
The proctor console's socket brings each of 40 candidates' status every 5 s, and its handler is:
A proctor switches to another browser tab for 30 minutes, then back.
What does pending hold when she switches back, and what happens next?
const pending = [];socket.onmessage = (e) => { pending.push(JSON.parse(e.data)); if (pending.length === 1) requestAnimationFrame(flush);};function flush() { for (const m of pending) apply(m); pending.length = 0; drawChanged();}Question 4 of 5
The proctor console shows each candidate's flag count and keeps lastSeq, the number of the last flag it has counted. GET /snapshot answers { seq, flags }; GET /events?after=n returns the flags numbered above n that the feed still keeps, which are those of the last 2 minutes. The socket sends each new flag once, to the sockets open at that moment, and the console closes its socket while its tab is hidden. A proctor hides the tab from 10:00 to 10:10.
Which path, run when the tab shows again, makes every flag count exact?
Question 5 of 5
Each candidate's laptop sends { id, status } every 5 s, and the proctor console handles it with:
Candidates c-4 and c-12 send a new question number with every status. Candidate c-30 has sent "writing" every 5 s since 11:03:00, after "reading" before. c-12's laptop lost its connection right after its 11:02:00 status.
At 11:05:00, which tiles show "stale"?
socket.onmessage = (e) => { const s = JSON.parse(e.data); const prev = tiles.get(s.id); tiles.set(s.id, { status: s.status, heardAt: now() }); if (prev?.status === s.status) return; changed.add(s.id); if (!queued) { queued = true; requestAnimationFrame(drawChanged); }};function drawChanged() { queued = false; for (const id of changed) { const t = tiles.get(id); draw(id, t.status, now() - t.heardAt > 15000 ? 'stale' : 'live'); } changed.clear();}Go deeper
Pro tip
- State the feed as numbers before drawing any box: how many tiles, and how many messages each tile gets per second at the busiest moment. When: the requirements phase of a dashboard round (the phases are
fsd-radio-framework's). Status: SELF; not run.
Source
- Front End Interview Handbook, Front End System Design (the curriculum row's URL): https://www.frontendinterviewhandbook.com/front-end-system-design · fetched 2026-10-01, saved as
sources/fih-overview.txt. "Data model: Describe the various data entities, the fields they contain and which component(s) they belong to". A guide's interview advice: valid as of 2026-10-01. - MDN, Server-sent events (the curriculum row's URL): https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events · fetched 2026-10-01, saved as
sources/mdn-sse.txt. "it's possible for a server to send new data to a web page at any time, by pushing messages to the web page" (step 2). Valid as of 2026-10-01. - web.dev, Optimize Interaction to Next Paint (the curriculum row's URL): https://web.dev/articles/optimize-inp · fetched 2026-10-01, saved as
sources/webdev-optimize-inp.txt. "limit what gets run to just the logic required to apply visual updates for the next frame" (steps 7–8). Valid as of 2026-10-01. - MDN, Window: requestAnimationFrame() method: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame · fetched 2026-10-01, saved as
sources/mdn-raf.txt. "requestAnimationFrame() calls are paused in most browsers when running in background tabs or hidden <iframe>s" (step 10); "requestAnimationFrame() is one-shot" and "When multiple callbacks queued by requestAnimationFrame() begin to fire in a single frame, each receives the same timestamp" (step 9). Valid as of 2026-10-01. - MDN, Page Visibility API: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API · fetched 2026-10-01, saved as
sources/mdn-page-visibility.txt. "An application showing a dashboard of information doesn't want to poll the server for updates when the page isn't visible" (step 11). Valid as of 2026-10-01.
Related lessons
Checked against the tools and docs as of 2026-10-01.