Code — how this page is built
Under the hood
The actual source of the components on this page, read straight from the repo. Copy it, read it, or open it on GitHub.
ReactionGameview on GitHub ↗
A small state machine (idle → waiting → ready → result) timing the gap between the green cue and your click with performance.now(); guards against early clicks. Best persisted to localStorage.
'use client';
import * as React from 'react';
const KEY = 'kaspirius:reaction:best';
type Phase = 'idle' | 'waiting' | 'ready' | 'result' | 'tooSoon';
export function ReactionGame() {
const [phase, setPhase] = React.useState<Phase>('idle');
const [ms, setMs] = React.useState(0);
const [best, setBest] = React.useState<number | null>(null);
const readyAt = React.useRef(0);
const timer = React.useRef<number | null>(null);
React.useEffect(() => {
const b = Number(localStorage.getItem(KEY));
if (b) setBest(b);
return () => {
if (timer.current) window.clearTimeout(timer.current);
};
}, []);
const click = () => {
if (phase === 'idle' || phase === 'result' || phase === 'tooSoon') {
setPhase('waiting');
timer.current = window.setTimeout(() => {
readyAt.current = performance.now();
setPhase('ready');
}, 1200 + Math.random() * 3000);
} else if (phase === 'waiting') {
if (timer.current) window.clearTimeout(timer.current);
setPhase('tooSoon');
} else if (phase === 'ready') {
const t = Math.round(performance.now() - readyAt.current);
setMs(t);
setPhase('result');
setBest((b) => {
const nb = b == null ? t : Math.min(b, t);
localStorage.setItem(KEY, String(nb));
return nb;
});
}
};
const bg =
phase === 'ready' ? 'var(--accent-olive)'
: phase === 'waiting' ? 'var(--accent-red)'
: 'var(--tile)';
const msg =
phase === 'idle' ? 'Click to start'
: phase === 'waiting' ? 'Wait for green…'
: phase === 'ready' ? 'CLICK!'
: phase === 'tooSoon' ? 'Too soon — click to retry'
: `${ms} ms — click to go again`;
return (
<div className="flex w-full max-w-md flex-col items-center gap-5">
<button
type="button"
onClick={click}
className="flex h-64 w-full items-center justify-center rounded-2xl border border-foreground/15 text-xl font-bold text-foreground transition-colors"
style={{ background: bg, color: phase === 'ready' || phase === 'waiting' ? '#fff' : undefined }}
>
{msg}
</button>
<div className="font-mono text-[11px] uppercase tracking-[0.12em] opacity-50">
best {best != null ? `${best} ms` : '—'}
</div>
</div>
);
}