▶ Live demo · apps.charliekrug.com/terseql
The daily SQL puzzle where shortest wins. One puzzle a day: a small schema, a plain-English goal, and a real SQLite database running in your browser. Getting the query working is the easy part. Your score is its UTF-8 byte length, and the board sorts ascending.
For developers and analysts who write SQL well enough that "correct" stopped being interesting.
Day one asks for each customer's total spend, highest first, skipping anyone who never ordered. The query you'd put in a pull request is 126 bytes:
SELECT c.name, SUM(o.amount) AS total
FROM customers c JOIN orders o ON o.customer_id = c.id
GROUP BY c.id
ORDER BY total DESCThat's the starting line. The alias goes, because ORDER BY 2 points at the column by position.
JOIN ... ON becomes a comma join and a WHERE. Whitespace the parser doesn't need goes:
SELECT name,SUM(amount)FROM customers c,orders WHERE customer_id=c.id GROUP BY 1 ORDER BY 2 DESC96 bytes, same rows, still passes every hidden fixture. Somewhere below that is a query you haven't thought of yet.
Solve it and you get a share card, which is the trail rather than the answer:
Terseql 2026-07-16 — Top Spenders
🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦 126
🟦🟦🟦🟦🟦🟦🟦🟦🟦 113
🟦🟦🟦🟦🟦🟦🟦🟦 101
🟩🟩🟩🟩🟩🟩🟩🟩 96
96 bytes (4 cuts)
🔥 4-day streak
https://apps.charliekrug.com/terseql
It carries no query text, so pasting it in a group chat can't spoil the day for anyone.
- A real engine, in your tab. sql.js (SQLite compiled to WebAssembly) executes every query client-side. Hit Run and the actual result table appears. There is no backend in the solve loop at all, and the WASM is compiled while you're still reading the prompt, so the first Run is as instant as the tenth.
- Hidden fixtures. Each puzzle ships seeded databases you never see, covering empty groups, ties, NULLs and negatives. A query that fits the visible sample and nothing else is told it passed the sample and failed a hidden case, and never which one. Working that out is the puzzle.
- Bytes, counted honestly. UTF-8 bytes, not characters, so a multi-byte glyph costs what it actually costs. The counter rolls digit by digit as you trim.
- A fresh database every Run. Write
DROP TABLE ordersif you like; the next Run starts from the same ground truth, and grading uses its own databases regardless. - Daily rotation. Puzzles are keyed to the UTC calendar date, so everyone solves the same one on the same day. Five are authored.
npm install
npm run dev # dev server
npm test # full suite
npm run test:coverage # full suite + a coverage report
npm run lint
npm run build # -> dist/ (landing page) + dist/app/ (the app)
npm run preview # serve the built site: landing at /, app at /app/The build is one self-contained directory using only relative paths, so it serves correctly from
a domain root or a subpath. To point the app at a shared leaderboard, set VITE_LEADERBOARD_URL
at build time; with it unset the app runs standalone on local personal bests.
- JavaScript, no framework. It's small enough that one isn't needed.
- sql.js: SQLite compiled to WebAssembly, run entirely client-side.
- Vite: dev server + static production build.
- Vitest: unit, DOM and real-engine integration tests.
docs/ARCHITECTURE.md: the code map, data flow and decisionsdocs/VISION.md: what this is and whydocs/DESIGN.md: the visual direction and tokensdocs/BACKLOG.md: what's built vs. planned
MIT. See LICENSE.
More of Charlie's projects → apps.charliekrug.com