Critical Review — snake
- Reviewed: 2026-08-14T20:59:53.606223+00:00
- Reviewer model: qwen/qwen3.7-max (temperature 0, independent of builder)
- Builder model: deepseek-v4-flash
- Objective verification: 10/10 checks passed
Process & bias controls
This review was produced by an independent frontier model in a sandbox.
Controls: blind context (no builder claims), deterministic rubric,
evidence-required scoring, independent factual verification by the
harness, and full transparency (prompt + raw output archived in review.json).
Scores
- correctness: 7/10
- fidelity: 8/10
- twist: 7/10
- ux: 5/10
- mobile: 8/10
- accessibility: 5/10
- performance: 9/10
- security: 9/10
- docs: 3/10
- originality: 6/10
Verdict: needs-work
### Summary
A competent Snake clone with a well-integrated combo multiplier twist, but undermined by a non-functional 'Try again' button, missing LICENSE for game code, and an experiments.json that doesn't even include the snake experiment. Core gameplay is solid; the surrounding polish and documentation are lacking.
### Issues
- [critical] (ux) The 'Try again' retry button shown on game over has no event listener bound to it. Clicking it does nothing. — index.html:84 defines <a id='snake-retry'>, but js/game.js only binds 'snake-restart' (line 174-175). No listener for 'snake-retry' exists anywhere.
- [major] (docs) No LICENSE file exists for the game code itself. Only a font license (Clear Sans / Apache 2.0) is present. — Glob for **/LICENSE* returns only style/fonts/LICENSE-ClearSans.txt. No top-level LICENSE or COPYING file.
- [major] (docs) experiments.json does not include the snake experiment — it only contains the 2048 entry. The experiment ID mismatch means any tooling relying on this manifest will not find snake. — experiments.json:3 — the only entry has id '2048'. No snake entry exists.
- [minor] (correctness) endGame() is called twice on win: once from spawnFood() when no empty cells remain, and again from step() line 113. This double-writes localStorage and sets over=true redundantly. — js/game.js:49 calls endGame() inside spawnFood(); js/game.js:113 calls endGame() again after spawnFood returns.
- [minor] (ux) The retry button (snake-retry) has role='button' and tabindex='0' but no keyboard activation handler (Enter/Space), so even if a click handler were added, keyboard users couldn't activate it without additional code. — index.html:84 — role='button' tabindex='0' with no keydown listener in game.js.
- [minor] (accessibility) The game canvas has no aria-label, role, or aria-live region to communicate game state to screen readers. The game is entirely inaccessible to visually impaired users. — index.html:88 — <canvas id='snake-canvas' class='snake-canvas'> with no ARIA attributes.
- [minor] (accessibility) No reduced-motion support. The score flash animation and food color change have no prefers-reduced-motion media query override. — style/main.css — no @media (prefers-reduced-motion) query anywhere in the file.
- [minor] (accessibility) Combo state is communicated only through food color change (from #f2b179 to #f65e3b), with no text or icon indicator on the canvas. The stats bar shows 'x1' when inactive which partially mitigates this. — js/game.js:198 — ctx.fillStyle = combo ? '#f65e3b' : '#f2b179'.
- [minor] (docs) No attribution to the original Snake game creators. The 2048 files credit Gabriele Cirulli, but game.js has no corresponding attribution for Snake's origins. — js/game.js:1-5 — header comment mentions ByJTT Experiment but no original author credit.
- [minor] (docs) Seven JS files (game_manager.js, grid.js, html_actuator.js, keyboard_input_manager.js, local_storage_manager.js, tile.js, application.js) are 2048 code not loaded by index.html — dead code in this artifact. — index.html:102-104 loads only game.js, lb-tab.js, x-embed.js. The other 7 files are never referenced.
- [minor] (ux) Stray orphan </script> tag on line 106 of index.html after the x-embed.js script block. Harmless but indicates sloppy markup. — index.html:106 — bare </script> with no matching open tag.
- [minor] (ux) index.html references /style/site.css (absolute path) which does not exist in the artifact, so shared site nav styling will be missing. — index.html:11 — <link href='/style/site.css'> but no such file in artifact.
### Strengths
- Core snake mechanics are correctly implemented: wall collision, self-collision with proper tail handling, growing, and progressive speed increase (js/game.js:81-113).
- Combo twist is well-designed with a 4-second chaining window, visible multiplier in the stats bar, and score flash feedback (js/game.js:99-107).
- Mobile support is thorough: responsive canvas with devicePixelRatio scaling, touch swipe with scroll prevention, on-screen D-pad hidden on desktop via media query (js/game.js:145-171, style/main.css:41,69-71).
- Input handling covers keyboard (arrows + WASD), touch swipe, and D-pad with 180-degree reversal prevention (js/game.js:124-171).
- Security-conscious X/Twitter embed sanitization strips scripts, iframes, event handlers, and javascript: URIs (js/x-embed.js:15-33).
### Recommendations
- Bind an event listener to the 'snake-retry' button so the game-over 'Try again' button actually works.
- Add a top-level LICENSE file for the game code and include attribution for the original Snake game.
- Add the snake experiment entry to experiments.json.
- Add aria-label and an aria-live region to the canvas to communicate game state (score, game over) to assistive technology.
- Remove the 7 unused 2048 JS files from the artifact, or move them to the 2048 experiment directory.
- Add a @media (prefers-reduced-motion) query to disable the score flash animation.
### Objective verification (harness)
- ✅ file:index.html
- ✅ file:js/game.js
- ✅ file:js/lb-tab.js
- ✅ file:style/main.css
- ✅ license:MIT n/a — original implementation, no MIT derivation
- ✅ tests:pass 5 pass, 0 fail
- ✅ selfcontained
- ✅ viewport
- ✅ x-share
- ✅ leaderboard