Examples menu

⌨️ Type Race

Race to type a PHP snippet first. Progress, WPM, and countdown all live-update for every racer — no client logic.

Race state machine — each race moves through waiting → countdown → racing → done. All transitions happen server-side; the client just sends keystrokes.
Custom scope per race isolates each race's broadcasts. Multiple races can run simultaneously — joining players are routed to an open race automatically.
Progress is server-computed — the client sends only the latest typed text; the server counts matching leading characters against the snippet. No snippet logic ships to the browser.
OpenSwoole countdown timer ticks 3…2…1 before the race starts, broadcasting to all racers each tick. The race clock also tracks elapsed WPM per player.
SESSION identity gives each racer a persistent name across tabs and refreshes. Joining the same race twice from two tabs counts as two racers.
Anti-cheat by design — the server holds the snippet truth and computes every progress value. Sending the wrong text just gives zero progress.
⌨️

Waiting for racers…

Click Start Race when everyone's ready. Open this page in another tab to race yourself!

Falcon60 (you)

⚡ Signals

username string SESSION

Racer handle, auto-assigned. Persists across tabs.

typedText string TAB

Current textarea value. Sent to server on every input event; never broadcast.

raceStatus string Custom race scope

"waiting" | "countdown" | "racing" | "done". Controls which UI panel renders.

countdown int Custom race scope

3…2…1 before start. Broadcast each tick.

🎯 Actions

updateProgress

Called on every input event. Server counts correct leading chars, updates this racer's progress and WPM, broadcasts to race scope.

joinRace

Joins the current open race (or creates one). Starts countdown timer when MIN_RACERS reached.

👁 Views

type_race.html.twig

Lobby, countdown overlay, racing textarea + progress bars, and results podium. All driven by raceStatus signal.