Demo type · 07

Prototype animation: watch a process unfold over time

Use this when a static diagram can't show movement — when the lesson is about what happens, in what order. Here: a web request travels through a load balancer to one of three app servers, with Play / Pause / Step controls.

This is a copyable exemplar, not a finished lesson. Lift the <section id="player"> block (and its slice of CSS + the <script>) into a real lesson built from assets/lesson-template.html — drop it where the template's id="try-it" demo goes.

1

The big idea


When a lot of people hit a busy website at once, no single computer answers everyone. A load balancer sits at the front door and hands each visitor's request to whichever back-end computer is least busy. The visitor never notices — they just get an answer.

A picture of boxes and arrows can show that this happens, but not the order it happens in. This demo lets you press Play and actually watch one request travel: in to the front door, out to a chosen server, the work gets done, and an answer comes back. Press Step to walk it one beat at a time.

Think of it like… a host at a busy restaurant. People arrive at the door (the load balancer), and the host glances around and seats each party at whichever table (app server) has a free waiter — so no single waiter gets buried while others stand idle.

Under the hood

The load balancer is a reverse proxy (e.g. Nginx, HAProxy, or a managed L7 ALB) terminating the client connection and opening its own connection to a chosen upstream. Selection here is least-connections; round-robin and weighted variants are common. A periodic health check removes an unhealthy upstream from the pool, so the chosen server is always one that recently passed a probe.

The "answer comes back" beat is the response flowing back along the same proxied connection. In practice the app server may also touch a shared database or cache before responding — omitted here to keep the running example to one request, one round trip.

2

Watch one request, step by step


Press Play to run it end to end, Pause to freeze, or Step to advance one beat. The chosen server lights up; Reset reshuffles which server gets picked.

ready
Client Load balancer app server #1 app server #2 app server #3
Read left to right: Client → Load balancer → one app server → and back. Press Play.

Ready. The load balancer will pick a server when you press Play or Step.

State machine, not a video

The motion is a tiny finite state machine in vanilla JS — four phases: arrive, route, process, respond. Each Step advances one phase and tweens the token's cx/cy with requestAnimationFrame and an ease-in-out curve. Play just auto-fires the next step on a timer; Pause clears the timer. There is no <video>, no GIF, no library — so it scrubs, steps, and resets deterministically.

Why animate instead of draw

A static SVG (the architecture pattern in svg-patterns.md) answers "what's connected." Animation answers "in what order, and which path this time." Reach for this demo type only when sequence or timing is the lesson; otherwise a labelled still costs less and reads faster.

Accessibility

The whole sequence is narrated in the aria-live="polite" caption, so a screen-reader user hears each beat without seeing motion. Controls are real <button>s, and prefers-reduced-motion drops the tweens to instant jumps.

3

The core of the wiring


The engine is small: a list of phases, a function that moves one token from A to B, and three buttons calling step(), play(), reset(). Here is the heart of it (the full version is the <script> at the bottom of this file).

// four ordered phases — the whole "process over time"
const PHASES = ['arrive', 'route', 'process', 'respond'];
let phase = -1, chosen = pickServer();   // which app server this run

function step() {
  if (phase >= PHASES.length - 1) return done();
  phase++;
  render(phase, chosen);            // tween token + light up nodes
}

function play() {                     // auto-fire the next step on a timer
  timer = setInterval(() => phase < PHASES.length - 1 ? step() : pause(), 1100);
}