Sudoku — Ten Implementations Compared

One puzzle, one Rust API, ten implementations across thirteen repositories. Eight are hand-built framework ports sharing a single component tree; one of those eight is the original the rest descend from. Two are single-file apps written entirely by Claude Code.

On the count. projects://sudoku counts its ten as the eight framework builds plus the two Custom Element variants (sudoku-p4t-wc, sudoku-s4e-wc). The ten here is a different set: the eight framework builds plus sudoku-cc and sudoku-juris, with the CE variants treated as variants. Both counts are correct about different things.

What all ten share

Every implementation fetches https://sudoku-rust-api.vercel.app/api/puzzle — an 81-character puzzle string and a ref field that is base64 of the 81 solution digits. Nothing solves anything; atob does the work, and a hard-coded fallback puzzle means a dead API never blocks a game.

All ten persist to localStorage under a project-namespaced key — proto-sudoku::data, sudoku-r3t::app-data, sudoku-cc:game-state. That is not tidiness. GitHub Pages puts every project on one shared eswat2.github.io origin, and an unqualified key had sudoku-cc adopting and deleting the Juris app's save.

Two families, one ancestor

The eight framework builds are not eight Sudoku apps. They are one app expressed eight ways, deliberately held constant so the framework is the only variable — the same component tree file for file (alert, alerts, button, cell, eswat2-io, header, keys, spinner, sudoku-board, tool-bar, tw-label), the same bag/labels/store/tw-version/tw utils, the same pnpm + Vite + Tailwind toolchain, even the same npm scripts. Only the state layer moves: @stencil/store, Preact signals, Zustand, solid-js/store, a Svelte writable, Pinia, and nanostores for both Lit and uce.

The Stencil build came first — 23 April 2021, five months before the next one. Its component filenames are the ones every later port kept, which makes it less a sibling than the template. It is also the only one that renders into Shadow DOM (shadow: true), which makes it the closest structural relative of sudoku-cc.

sudoku-cc and sudoku-juris are not ports of those eight. They rebuild the game under a constraint the others never had — one file, no build step, no package.json — and they were written by Claude Code rather than by hand.

Where the code diverges

The eight framework ports sudoku-cc & sudoku-juris
Board model A flat list of 81 cell objects with precomputed row/column/box maps; peer highlighting is a lookup Three parallel 9×9 number[][] arrays — clues, live board, answer key; highlighting is recomputed over all 81 cells per selection, separating row, column, box and same-number
Entry A Keys component under the board, listing the values the cell's row, column and box don't use The same contextual list, rendered inline beside New Puzzle
Validation Deferred and manual — a Check button is present from load (list.length === 81, the board's cell count) and erases every wrong entry when pressed; only the win raises an Alert Immediate, against the decoded solution; a wrong entry reverts itself after 1s
Styling Tailwind through a tw() helper, on the clrs palette CSS inlined in template literals, duplicated across all three render paths (renderGrid, showLoadingState, showError)
Accessibility Inconsistent — <label> in Stencil, Preact, React, Solid, Vue; <span role tabindex> in Svelte; bare <div>/<span> in Lit and uce. The keypad is a real <button> in all eight, but only Svelte can reach a cell from the keyboard None: no ARIA, no keyboard path, no focus management; cells and keys are all plain <div>s

Three deploy models

The seven Vite ports build locally and deploy by hand: cd dist; ln -s ../.vercel; vc --prod. One target, and the deployed artifact is a hashed bundle that cannot be diffed against the repo.

sudoku-cc and sudoku-juris treat a push to main as the whole deploy — Vercel serves index.html statically and GitHub Pages serves the same file from root. Because nothing is built, the deployed bytes are exactly the repo's bytes, so curl -s <url> | md5 against md5 index.html is a valid freshness check.

Stencil is the odd one out and the most interesting. proto-sudoku-wc builds to an npm package and publishes it (0.1.293); a second repo, wc-sudoku, deploys the actual site — 875 bytes of HTML pointing at jsDelivr, pinned to that exact version, with a single <proto-sudoku /> in the body. Shipping a change means publishing to npm, then bumping the version in two URLs and the <title>.

Measured payloads

Gzipped JS and CSS, from fresh local builds on 5 September 2026. wc-sudoku was measured from a cold load of the live site rather than from the package, because Stencil lazy-loads its chunks. The sudoku-juris row was re-measured on 7 September 2026, when that app moved from Juris 0.88.2 to 0.9.0 and switched to the minified build the package ships, taking its runtime from 28.4 KB gzipped to 13.7 KB. juris.js and juris.mini.js are different files and the repo had been loading the unminified one; the swap was worth more than the version bump. Every other row stands as measured on the 5th.

App Gzipped Note
sudoku-cc 9.0 KB zero dependencies, whole app
sudoku-uce 19.5 KB
sudoku-sld 20.1 KB
sudoku-juris 20.5 KB 6.7 KB app + 13.7 KB runtime from unpkg
sudoku-p4t 22.2 KB
sudoku-lit 25.7 KB
sudoku-s4e 29.8 KB
sudoku-vue 40.0 KB
wc-sudoku 40.4 KB 0.4 KB page + 39.9 KB component from jsDelivr
sudoku-r3t 74.2 KB

Two of the ten deliver a nearly empty page and pull the real payload from a public CDN, and they arrived at that shape four years apart. The Juris result cuts both ways: its app code genuinely is smaller than the Web Components version — 6.7 KB against 9.0 KB, about 26% less for the same game — but the 13.7 KB framework still makes the delivered page 2.3× the size of sudoku-cc. Neither CDN tag carries an integrity hash.

What each family is for

Holding the app, the components, the utils and the toolchain constant is the whole value of the eight: change one variable — the reactivity model — and the diff between any two repos is a genuine framework comparison rather than a comparison of two people's taste. That only works because there was a template to hold constant, which is what the Stencil build has been since 2021.

The two Claude-written apps answer a different question: not which framework, but what an agent produces under a hard constraint. Which is why sudoku-cc shipping the smallest payload of the ten is not really a win over React — it is the constraint doing its job. The more interesting result is at the other end. sudoku-juris hands its lead straight back to a CDN framework, though far less of one than it looks: on the minified build it ships 2.3× sudoku-cc and beats six of the eight framework apps outright. wc-sudoku reached the same thin-page-plus-CDN shape from the opposite direction four years earlier and lands 19.9 KB heavier.

Written September 2026 from the repository working trees and git history. Sizes are gzipped JS + CSS from local production builds made the same day, except wc-sudoku, taken from the browser network panel on a cold load. Source line counts are not comparable across the two families and are omitted here for that reason: for the framework builds they would cover only src/, and for the single-file apps the entire index.html.