Katel
One shape, one cut.
Katel is a daily puzzle in the spirit of Wordle, but for geometry. Every day there’s a new shape. You drag a single straight line across it and get scored on how close you came to splitting it into two halves of exactly equal area. One attempt, no undo, no retry. The score is exact - not estimated, not approximated - and the whole project is built around keeping it that way.
What it is
- One puzzle per UTC day. Every player worldwide gets the same shape, generated deterministically from the date.
- One cut. The cut is an infinite line, not just the stroke you drag. On commit it extends across the whole shape, and the two halves slide apart to show the split.
- Scored to one decimal. 100.0 is a perfect bisection. Grades run from Way off through Close-ish, Good, Great, Excellent and Flawless up to PERFECT.
The shapes aren’t simple polygons: they’re concave, have holes, and sometimes come in several disjoint pieces. There are eight families - blobs, stars, gears, donuts, multipart shapes, rotated letters, polyominoes and spirals - and each day also draws its own colour from the same seed.
Why it exists
Katel tests one skill: finding where an irregular shape balances, by eye. That only works as a game if the number is trustworthy. A score that’s off by a fraction of a percent - or worse, one that looks plausible while being wrong - would ruin the premise.
So Katel is deliberately small on the surface and strict underneath: one shape, one cut, one number, and that number is exact.
Core features
| Feature | What it does |
|---|---|
| 📐 Daily shape | A new deterministic puzzle at midnight UTC, the same for everyone. |
| ✂️ One-shot cut | Drag once, release, done. The line is drawn exactly where your pointer went, then extended to the full chord on commit. |
| 🎯 Exact scoring | Split percentages always sum to 100, and a score never rounds up to 100.0 - anything short of a true bisection tops out at 99.9. |
| 📊 Stats & streaks | Games played, average, best, current streak and a grade distribution, stored locally in the browser. |
| 📤 Share | Renders the finished cut to a PNG and hands it to the native share sheet, with clipboard, download and plain-text fallbacks. The share text carries no shape data, so it can’t spoil the puzzle for other time zones. |
| 🎨 Daily colour | Each day draws its own hue; lightness and contrast stay fixed so the two halves remain distinguishable in greyscale and for every kind of colour vision. |
| 🌗 Light & dark | Theme toggle with no flash on load, and reduced-motion support. |
Technical details
Stack
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router), React 19 |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS v4, OKLCH palette |
| Rendering | SVG on screen, Canvas 2D for share images |
| Storage | localStorage only - no backend, no account |
| Testing | node —test, running the TypeScript engine directly via Node’s type stripping |
| Deployment | Nixpacks |
Engineering decisions
- A pure engine.
lib/katel/is plain TypeScript with no React, Next or DOM imports. Geometry, shape generation, scoring, seeding, stats and the game state machine are all pure functions. An ESLint fence and a purity test that greps the sources (noMath.random, noDate.now) keep it that way, because determinism fails silently. - Area by half-plane clipping. Each ring is clipped with Sutherland–Hodgman and summed with a signed shoelace. Holes subtract by winding sign with no special code path, and the seam edges between disconnected pieces contribute exactly zero. The second half is defined as
total - areaNeg, so the two sides add up bit-exactly. - Verified three ways. The shipped clipper is checked against a per-edge integration and an independent scanline quadrature, plus closed-form fixtures, rigid-motion invariance and a bisection round-trip at 72 angles per fixture.
- No y-flip. Shape space is SVG space. A double flip renders correctly while negating every area - a wrong score that still looks right - so the flip was removed entirely rather than guarded against.
- Valid by construction. Shapes are built radially, traced from grid cells, or swept as a parametric band - techniques that can’t self-intersect. Letters are rotated by a seeded angle, because an axis-aligned symmetric letter hands a free 100 to anyone who cuts down the middle.
- Difficulty filters. Generated shapes are rejected if their bisectors nearly all pass through one point, which would make any cut through the centre a winner.
- Pinned determinism. The day index is integer division on epoch milliseconds, with no
Dateinvolved, and a golden file pins the first 31 days so any change that would reshuffle everyone’s puzzles fails loudly.
Get & use Katel
Play
Open katel.ntrapp.net. No sign-up, no install - a new shape appears every day at midnight UTC, and your stats live in your browser.
How to play
- Look at the shape and find where you think it balances.
- Drag a straight line across it and release. The line extends infinitely, so a short drag in the right direction is enough.
- See your split and score, then share your result.
You get one cut per day - take your time before you release.
Katel - one shape, one cut.