Test your speed. Own the dark.
A minimalist, dark-aesthetic typing speed test built with React, TypeScript, and Tailwind CSS. Inspired by Monkeytype — built with a distinct Iron Ghost / Dark Zen visual identity.
- Two test modes —
words(fixed word count) andtime(countdown timer) - Reactive flame — a live SVG flame that responds to your WPM, combo, and errors in real time
- Glitch intro screen — animated character-reveal title sequence on first load
- Live WPM / timer display — shows current speed or remaining time while you type
- Full result screen — WPM, raw WPM, accuracy, consistency, time, word count, and char breakdown
- Consistency tracking — WPM is sampled every 2 seconds and scored as a coefficient of variation
- Customisable caret — line, block, or underscore; optional smooth animation
- Punctuation & numbers — toggle modifiers to increase test difficulty
- Keyboard-first UX —
Tab + Enterto restart from anywhere,Escapeto reset mid-test - Smooth screen transitions — crossfade between intro → typing → result screens
| Layer | Choice |
|---|---|
| Framework | React 18 + TypeScript |
| Styling | Tailwind CSS (custom design token config) |
| Fonts | JetBrains Mono · IBM Plex Sans |
| Icons | Lucide React |
| Build | Vite |
src/
├── components/
│ ├── TypingArea.tsx # Typing canvas, caret rendering, live WPM
│ ├── ResultScreen.tsx # Post-test stats display
│ ├── ConfigBar.tsx # Word count / time presets, modifiers
│ ├── Header.tsx # Mode tabs, restart, settings
│ ├── Footer.tsx # Keyboard hint, version
│ ├── SettingsModal.tsx # Caret style, live WPM, smooth caret toggles
│ ├── GhostTyperIntro.tsx # Animated intro / splash screen
│ └── flame.tsx # Reactive SVG flame engine
├── hooks/
│ ├── useTypingEngine.ts # Core typing state machine (reducer)
│ └── useTimer.ts # Countdown timer hook
├── lib/
│ └── wordLists.ts # Word bank + list generator
├── styles/
│ └── globals.css # CSS variables, base styles, animations
└── App.tsx # Root layout, screen orchestration
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run buildAll typing state lives in a single useReducer inside useTypingEngine. It handles:
- Character-by-character correctness tracking
- Word advancement on
Space, backspace across word boundaries - WPM sampling every 2 seconds (used for the consistency score)
- Automatic finish detection when the last word is completed
The flame at the bottom of the screen is a fully procedural SVG rendered with requestAnimationFrame. It responds to:
| Signal | Effect |
|---|---|
| Higher WPM | Flame grows taller, shifts from purple → orange |
| Combo streak | Increases flame spread and ember count |
| Typing error | Flame flashes cold blue |
| Idle | Low, slow purple flicker |
WPM is sampled every 2 seconds during a test. At the end, the standard deviation of those samples is expressed as a coefficient of variation (CV), then inverted to produce a 0–100% consistency score. 100% means perfectly steady pace.
| Key | Action |
|---|---|
Tab + Enter |
Restart test (works from any screen) |
Escape |
Reset current test |
Space |
Advance to next word |
Backspace |
Delete last character (or return to previous word) |
Open via the gear icon (top right):
- Caret style —
line/block/underscore - Live WPM — show current WPM while typing (words mode only)
- Smooth caret — animate caret movement between characters
GhostTyper uses a custom design language called Iron Ghost / Dark Zen — calm, precise, dark, and alive. Key principles:
- Near-black base (
#080808) with cool neutral text (#e2e2e2) - Single accent colour — deep violet (
#9955ee) used sparingly - Monospaced type for all data; humanist sans for UI chrome
- All animations unified to
120ms, ease-based, never bouncy - Flame and caret are the only "live" elements — everything else is still
Full design spec: see src/styles/style.md and src/styles/ui-generation-guide.md.
MIT

