Tap the dot. Trace the line. Feed the void.
A reflex-based web game where precision meets speed. Trace lines shooting from a pulsing red dot before they fade — grow a black hole, stack multipliers, climb the leaderboard.
- A red dot pulses on the edge of a circle
- Tap it — a line shoots outward
- Drag your finger along the line before it fades
- Accuracy + speed + coverage = score
- The black hole grows. The timer shrinks. Repeat.
| Classic | Arcade | |
|---|---|---|
| Line scaling | +1 line per tier (every 100 rounds) | +1 line every round (max 10) |
| Difficulty curve | Slow burn | Rapid escalation |
| Leaderboard | Separate | Separate |
| Best for | Endurance | Intensity |
BASE = accuracy × 400 + coverage × 200
ACC BONUS = 95%+ ×1.5 · 90%+ ×1.4 · 85%+ ×1.3 · 80%+ ×1.2 · 75%+ ×1.1
TIME BONUS = seconds remaining × 100 (final line only)
STREAK = ×1.1 per completed line (2nd line ×1.1, 3rd ×1.2, ...)
COMPOUND = ×1.5 (bonus line only)
LEVEL MULT = increases every 10 rounds
FINAL = (BASE × ACC_BONUS + TIME_BONUS) × LEVEL × STREAK × COMPOUND
In multi-line rounds, dots appear staggered — tapping one reveals the next. The timer starts on your first tap and runs continuously.
- Complete all required lines → round clear
- Last line is always a bonus — optional, but worth ×1.5 compound on top of the streak
- Miss the bonus? No penalty. Time runs out after required lines done? Still counts.
Gameplay
- Two modes: Classic (endurance) and Arcade (intensity)
- Multi-line rounds with staggered dot reveal and streak multipliers
- Bonus line compound system
- Checkpoint + retry system (3 retries, bonus retries for 95%+ accuracy)
- Timer decay per round
- First-fail tutorial
Audio
- Full Web Audio API synthesis — no audio files
- Ambient drone, laser swish, success thrum, fail tone, score chimes, accuracy shimmer
- Dot pulse synced to ambient LFO
Visuals
- Black hole canvas: 4 wobble layers, 40 accretion particles
- Portal effect on round clear — color-coded by bonus count
- HUD shake on low timer
- Background blob animation
i18n
- 12 languages:
ENGESPFRADEUPORITAJPNKORZHORUSARAHIN - Auto-detected via
navigator.language
the-hole/
├── index.html ← The entire game (standalone, ~2300 lines)
├── CHANGELOG.md ← Version history
├── README.md ← You are here
└── functions/
└── api/
├── scores.js ← GET /api/scores?mode=classic|arcade
└── submit.js ← POST /api/submit (server-side replay)
Zero build step. React 18 + Babel loaded from CDN. One HTML file. Deploy anywhere.
The client never sends a score. It sends raw round data:
| Data sent | Purpose |
|---|---|
dotAngle, lineAngle, lineLength |
Round setup |
dragPath (array of {x, y}) |
Player's actual trace |
circleR, cx, cy |
Layout context |
elapsed |
Timing |
The Worker replays every round from scratch. Server-side checks:
- Path too perfect → rejected (>99.5% acc + >95% coverage with <8 points)
- Elapsed < 0.05s on later rounds → rejected
- Path teleport (>200px gap) → rejected
- Rate limited: 1 submit per 5s per IP
- Profanity filter on names (PurgoMalum API, client + server)
1. Create KV namespace
wrangler kv:namespace create SCORESOr: Dashboard → Workers & Pages → KV → Create → name it SCORES
2. Deploy
wrangler pages deploy . --project-name thehole3. Bind KV
Pages project → Settings → Functions → KV namespace bindings:
- Variable name:
SCORES - Namespace:
SCORES
Redeploy after binding.
4. Verify
# Should return {"scores":[]}
curl https://<domain>/api/scores?mode=classic
# Should return {"scores":[]}
curl https://<domain>/api/scores?mode=arcade| Method | Path | Params | Description |
|---|---|---|---|
GET |
/api/scores |
?mode=classic|arcade |
Top 10 scores for mode |
POST |
/api/submit |
JSON body | Submit raw game data |
{
"name": "PLAYER",
"mode": "arcade",
"rounds": [
{
"dotAngle": 1.23,
"lineAngle": 1.45,
"lineLength": 128,
"dragPath": [{"x": 200, "y": 300}],
"circleR": 48,
"cx": 200,
"cy": 350,
"elapsed": 0.84
}
]
}Response:
{"success": true, "score": 4200, "rank": 3}| Key | Contents |
|---|---|
leaderboard:classic |
Top 10 classic scores |
leaderboard:arcade |
Top 10 arcade scores |
ratelimit:{ip} |
Rate limit flag (TTL 10s) |
| Layer | Tech |
|---|---|
| Frontend | React 18, Babel (CDN), vanilla CSS |
| Audio | Web Audio API (oscillators, gain nodes, noise buffers) |
| Canvas | 2D context for black hole rendering |
| Backend | Cloudflare Pages Functions |
| Storage | Cloudflare Workers KV |
| Build | None |
CC BY-NC 4.0 — Free to use and modify with attribution. No commercial use.