Commit History
-
fix(onboarding): tidy the welcome modal layout (#46)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureVisual polish on the welcome card: - Drop the inline ⏎ glyph; the sentence now reads "the Enter button" in words, leaving a single carriage-return icon (the keycap) so the two no longer compete. - Balance the title across its two lines (text-wrap: balance) and give the boxed "targets" room to breathe. - Lighten the "targets" box (2px rim, 1° tilt) so it echoes the HUD target chips instead of overpowering them. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>3037fcd -
feat(onboarding): add a line-art welcome modal on game open (#45)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureGreet the player with a hand-drawn paper-and-ink card over the dropping-in board: - Title "Get the LLM to guess all the targets", with "targets" boxed like the real HUD target chips. - Body "Jump on the ⏎ button to submit your description", with an inline carriage-return keycap echoing the close button. - A ⏎ close button rendered like the actual submit tile (soft ink, dashed "press me" rim, centred glyph). Closes on the ⏎ button, Enter/Escape, or a backdrop tap; gameplay hops are held off (handleDir guard) until it's dismissed. Pure DOM/CSS over the existing overlay pattern — no Modal creds needed. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>8429f6a -
feat(levels): add the countries board with the column-shuffle mechanic (#44)
Ben BlakerUnverified signature## What & why Board four — **`countries`** (`order=3`, reached after the subjects board). The judge reads which country a sentence sounds like: USA, Russia, Italy, France, India, China, Korea, England. ### New mechanic — the `shift` / column-shuffle tile The user's `[swap]` tile, named **`shift`** in code so it doesn't collide with the existing off-edge board-navigation *swap tiles*. - Shift tiles render as hand-drawn keycaps stamped with **two stacked accent ⇨ arrows** (wordless, dashed "press me" rim). - The top row is all structural: a blank `start` in the middle flanked by four shift tiles. Hopping onto one **rotates every row UNDER the top row one column to the right**; the rightmost cap of each row **wraps around to become the leftmost**, arcing up-and-over the caps that slide in beneath it. The top row never moves. - Landing on a shift tile costs one budget hop and appends no word (consistent with `wings` / every land, and routed through #42's new `settleIdle()` …3663d53 -
feat(layout): centre the sentence and unify the desktop/tablet board (#43)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureCentre the in-progress sentence and anchor it a fixed gap below the target checklist (tracking the measured HUD height --hud-h) instead of the fixed top-left position. Extend the desktop layout down to phone widths: move the compact media-query breakpoint from 1240px to 560px so tablet/medium windows keep the generous desktop fonts rather than the cramped compact layout. To stop the board colliding with the wrapped checklist + sentence at those widths, the stage drops below the chrome adaptively — full height when the HUD is a single row (biggest board on wide screens), lowering only as --hud-h grows. The camera re-centres the board in that band on wide/tall windows; mobile and landscape framing are unchanged. Verified with Playwright on the emotion and animal boards at 1280 / 1000 / 900 / 640px and on portrait + landscape phones: the sentence is centred and clear of the targets, the board clears the sentence, no collisions, and the mobile layout is preserved. Co-authored-by: Claude…cfa0c79 -
feat(portal): spinning-spiral teleport tiles on the animal board (#42)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThree spinning ink-spiral portals on the animal board (board 2), each linking to the next clockwise (top→right→bottom→top). Hopping onto one shrinks the doodle into the source spiral and expands it out of the destination. "portal" is a new reserved tile word (wordless, never reaches the judge); the landing hop costs budget. Spirals spin in the render loop (mesh spin, no texture redraw). Rebased onto #41 (the wings board): #41 reserved the word "wings", which collided with the literal "wings" the layout placed at animal (3,2) — renamed that cell to "winged" so the animal board stays portal-only. Verified in-browser (Playwright): all three clockwise links walked live, spirals spinning, shrink-in/expand-out teleport, "winged" renders as a normal word, 0 console errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>8d3ebb8 -
feat(levels): add the subjects board with the wings flight mechanic (#41)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureBoard three (subjects, order=2), reached after the animal board. The judge reads which academic subject a sentence sounds like (maths, science, philosophy, economics, literature, languages, art, music). New mechanic — wings: - `[wings]` tiles render as hand-drawn accent wing keycaps (wordless, like start/⏎). - Hopping onto one lifts the doodle into the air: it sprouts wings (new "fly" pose) and rises to cruise altitude, wings slowly flapping on their own timer (independent of the idle "boil", so they keep beating mid-hop). - The next two hops glide FREE — airborne, no word appended, no budget spent. - The third hop descends "down from the sky" and lands for real, e.g. onto a ⏎ to press submit. Wings are a clean exit: lock in the sentence you've written, then soar over the rest of the keys to the button without typing them. Flight state resets on board change, death, fresh attempt, and swap-tile transitions. Adds takeoff (soar) and wing-beat (flap) sounds. Verified locall…45107cc -
fix(mobile): responsive feelings board — wrapping checklist, fuller board, sharper render (#40)
Ben BlakerUnverified signature- Targets checklist wraps to a full-width second row (title + counter keep row 1); board/prompt drop below via a --hud-h CSS var. - Compact breakpoint raised 560px → 1240px so mid-width windows/tablets stop wrapping big chips over the board; desktop ≥1240px unchanged. - Portrait board trims the camera side-margin to fill ~85% of the width without clipping edge columns. - Framebuffer pixel-ratio cap raised 2 → 3 so the board isn't soft on dpr-3 phones (textures stay at 2×). - Added CLAUDE.md (agent orientation).007f851 -
perf(render): smooth animation by gating and staggering the sketch boil
The boil redrew all ~16 hi-dpi (768px) tile canvases and re-uploaded their textures to the GPU in a single setInterval tick, 3x/second. Measured on HF/Safari that was a ~70ms main-thread stall every ~340ms — frame spikes of 40-94ms spaced one boil-interval apart — which dropped frames mid-animation and read as choppy, jagged, disjointed motion. Two changes, no loss of the hand-drawn liveliness: - Only boil while idle: during a hop / judge bob / verdict the motion already carries the life, and a stalled frame mid-motion is precisely what reads as jank, so those frames are left for the animation. - Round-robin a small fixed slice (2 of ~18 surfaces) per tick instead of the whole board at once, so no tick redraws/uploads more than ~2 textures. Re-measured: severe (>40ms) spikes during motion essentially gone, jank frames (>25ms) cut ~3x, median steady at ~17ms (59fps). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>559f3b3 -
fix(prompt): render the written sentence un-clipped in Safari
The chip write-on revealed text by animating clip-path with negative insets, then removed the clip with clearProps. Inside HF's iframe under Safari/WebKit that loses both ways: Safari clamps negative clip-path insets to 0, and it won't repaint a clip-path that's later removed until a manual reflow. So the just-written word stayed shaved off on the right until you inspect-element — every fresh load. Reveal by sweeping the right inset 100%->0 and END on a plain inset(0): no negative values, never removed. Pad .sq-chip-text (cancelled in layout) so inset(0) wraps every glyph, slant and descender with room to spare. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>5d9a0d4 -
fix(fonts): preload both Caveat weights before boot
document.fonts.ready only waits for faces already in active use, and the Google Fonts stylesheet loads async — so at boot Caveat 600 (the verdict card) wasn't loaded yet, and the card rendered in a fallback face then reflowed once the webfont landed. Explicitly request every weight we use (Patrick Hand 400, Caveat 600 + 700) so all text has final metrics before anything lays out or animates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>b57726d -
fix(mixer): keep the audio mixer open while you use its controls
Two ways the mixer dismissed itself mid-use: - A quick close→reopen landed in the 0.15s close fade: the stale fade's onComplete still ran and re-hid the popover (display:none) while popOpen stayed true. The next tap on a now-invisible slider fell through to the board, and the outside-tap handler closed the mixer. openPop() now kills any in-flight close, and the close onComplete bails if we've reopened. - Inside HF's iframe under Safari/WebKit a slider's pointer capture can retarget the pointerdown so the outside-tap handler's .contains() check reads false and a drag dismisses the mixer. Stop pointerdowns that start in the popover from reaching that handler at all. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>371be62 -
fix(embed): keep the HF board fit scroll-invariant (#32)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThe embed-fit `avail` folded in `info.scrollTop`, so the board refit on every parent scroll tick. Across a scroll only `scrollTop` changes (`clientHeight`/`offsetTop` are constant), so each tick swung the height and called `renderer.setSize()`, reallocating the WebGL drawing buffer — the "lags much more on HF" jank, with the board reframing mid-scroll clipping the canvas-baked keycap text. A real HF page is always scrollable, so it fired constantly; localhost has no iframe and never reproduced. Drop `scrollTop` from `avail` (now `max(0, clientHeight - offsetTop)`, preserving the "fit below HF's header" intent) and guard `apply` with a `lastKey` so pure-scroll getPageInfo events early-return before any fit/reflow. Genuine viewport changes (resize/rotate) still shift `clientHeight` and refit. Verified in a cross-origin iframe-resizer-v4 repro: buffer churn while scrolling drops from 4 distinct sizes to 1 stable size, the resize path still adapts (738->532->892), and the embedded board …8d256bc -
feat(audio): replace synth bg music with the Cipher2 track (#28)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureSwap the procedural C-pentatonic synth loop for a recorded track, downsampled to 128 kbps (~3.7 MB). It's too big to inline as a data URI like the pen-scratch SFX (that would bloat every page load), so it streams from static/ via gr.set_static_paths and the file route, with the URL resolved against the Gradio root so it holds up inside the HF Spaces iframe. A looping <audio> element feeds the existing musicGain bus, so the in-game mixer (slider, mute, localStorage persistence, first-gesture unlock) drives it unchanged. The pause-on-mute is deferred until the pending play() settles, so a fast mute->unmute mid-load can't wedge the element. Verified locally (Gradio 6.18, Playwright): the file route serves audio/mpeg with range support, the track plays and loops in real time through the bus, and mute/unmute -- including rapid toggles during load -- behaves correctly, with no console errors. HF-iframe streaming still needs a real deploy to confirm. Co-authored-by: Claude Opus 4.8 (1M con…fc31f3c -
fix(ui): enlarge target words and center them when they wrap (#30)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThe target words in the HUD rendered at 20px on laptop/desktop, which was too small to read at a glance. Bump them to 32px (the dominant HUD text), keep phones at 20px via the existing mobile media query so the compact single-line HUD is unchanged, and add justify-content:center so wrapped lines center under each other instead of left-aligning. Verified in-browser at 1440x900 (computed 20px->32px, no HUD collision), 390x844 (still 20px, no overflow), and a wrap-forcing 580x900 (two lines, both centered on the same x). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>c949a43 -
fix(layout): keep the grid centred when the forward swap tile appears (#29)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signaturePreviously the board shifted left by half a gap whenever a next board existed, so the grid only re-centred after the swap tile was added. Pin the board at x=0 instead; the camera already frames x=0 with the swap tile's width, so the grid reads centred from the start and the lone tile sits in the right margin. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>a5f7f73 -
feat(ui): retitle the out-of-hops stamp "context window exceeded" (#27)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureLeans into the LLM theming: the death card's red stamp now reads "context window exceeded" instead of "out of hops!". Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>efe291e -
feat(audio): pen-scratch + keycap-press sound as each word is written (#26)
Ben BlakerUnverified signaturePlay a real pen-nib recording (trimmed ~0.37s clip, inlined as a data URI) on each word's handwriting reveal, alongside the keycap-press thock. Rides the sfx bus so the in-game mixer levels/mutes it. Verified in-browser (sample plays once per word, no console errors); 18 unit tests pass.6f2eb74
-
feat(nav): swap boards by hopping a one-way progression tile (#25)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureReplace the two bottom-corner nav buttons with an in-world keycap that sits just off the board edge. Once every target on a board is collected, a "→ <next>" tile appears; hopping onto it transports the doodle to the next board's start tile and rains the rest of the keycaps in from the sky, staggered by distance, before play resumes. Progression is one-way (no back tile). The same drop-in plays as the board intro. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>b9f18ee -
fix(ui): stop clipping the ! and ? sentence chips (#22)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThe chip write-on reveal animates a clip-path inset; gsap leaves the end state (`inset(-20% -10% -20% -10%)`) applied permanently. The right inset is a percentage of the chip's box, so for a single-character `!`/`?` tile (~12px wide) it's ~1px — too little to clear Caveat's forward slant, and the lean stays clipped forever. Clear the inline clip-path once the reveal finishes (gsap `clearProps`), so no residual clip lingers on any chip. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>8633182 -
feat(audio): background music and an in-game volume mixer (#21)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureAdd a fully procedural background music loop (generative C-pentatonic melody over a soft I–vi–IV–V bass, on its own Web Audio bus) and an in-game volume mixer. Tapping either top-right icon opens a hand-drawn popover with two wobbly-ink sliders — music and sfx — each with a mute icon; it closes on an outside tap or Escape. Each channel carries a 0..1 volume on an independent bus, squared into a perceptual loudness curve; dragging snaps the gain live while mute/unmute ramps it and restores the prior level. Levels persist in localStorage and the bar icons show a red ink slash when a channel is at zero. Music defaults on and starts on the player's first gesture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>7d3cc83 -
fix(ui): restore the ⏎ submit tile and a solid start outline (#20)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureRevert the submit tile's label from "=" back to "⏎" (the carriage-return glyph) across the level grids, board engine, tests, and docs. The judge prompt is "<sentence> is the same as" now, so the tile is a purely visual marker — the rename touches no scoring logic. Also drop the dashed rim on the start tile so it reads as a normal solid keycap; only the submit tile keeps its dashed "press me" cue. 23 fast tests pass; verified both boards render in-browser (fake judge). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>48c6837 -
fix(layout): keep tiles clear of the hop hint line (#19)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThe stage was full-bleed (inset: 0) and the board frames itself hugging the stage bottom, so the bottom tile row rendered straight under the "arrow keys / WASD to hop" (or "swipe to hop") line. Reserve that bottom band as non-play area; the existing camera framing reflows the board into the shorter stage, and the death-slide now exits the canvas above the hint. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>491673f -
fix(layout): remove residual vertical overflow in the HF Spaces iframe (#18)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThe previous embed fit filled the parent's full viewport, but the app iframe sits below HF's header (offsetTop) and Gradio's shell (.main.fillable/.wrap) is a constant few px taller than our component — so the page still scrolled ~96px. Fill the actual slot instead: parent viewport minus the iframe's offset below the top, then subtract the measured shell chrome (body height − root height) so the whole document equals one screen. Clamp to one viewport and cache the chrome to avoid oscillation. Verified 0 overflow on desktop (818=882−64) and mobile (780=844−64); the full board, HUD and hint fit on a single page either way. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>da194a2 -
fix(layout): keep the board on-screen in the HF Spaces iframe (#16)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureHF embeds the Space in an iframe driven by iframe-resizer, which sizes the iframe to its content. The board's `100dvh` root then resolves to the iframe's own height, so each measure-and-grow cycle made it taller without bound — the vertically centered 3D board ended up thousands of px below the fold ("it all drags down"). Local runs have no iframe, so `100dvh` was the real window and the bug never showed. When embedded, let the document be content-height and pin the game to a fixed pixel height (the parent's visible viewport via iframe-resizer's getPageInfo, with an instant innerHeight/screen fallback) so the iframe settles instead of feeding back on itself. The non-embedded path is untouched. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>83e6847 -
refactor(ui): drop redundant labels and the extra check tick (#15)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureThe "targets:", "sentence:", and "hops:" labels stated the obvious — the checklist pills, the prompt strip, and the "N/budget" counter each read clearly on their own — so remove them and the now-dead CSS (including the tiny-phone rule that only hid the "targets:" word). A collected target already reads as collected via the fade + strikethrough, so drop the redundant green ✓ tick. checkOff now gives a brief scale pop on the pill itself for feedback in place of the tick animation. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>ff1852e -
refactor(judge): simplify to a "<sentence> =" prompt; rename ⏎ tile to =
The judge prompt is now just the assembled sentence followed by " =", and the board's candidate labels alone constrain the answer (emotions on one board, animals on another). The submit tile changes from ⏎ to = so the tile you hop onto is literally the prompt's operator. - levels: Level drops category/few_shot/question, keeping grid + labels. Grids use "=" for submit tiles. - judge: score_labels(sentence, labels) sends one message, no examples. - engine / tests / README / logprobs probe: ⏎ -> =. 23 fast tests pass; verified both boards submit onto "=" in-browser (fake judge). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>edcc51c -
feat(levels): modular drop-in level API + animal board
Make adding a board a one-file drop-in, and ship a second level. - levels/ package: each board is a self-contained Level (grid + its own judge spec — category, few-shot, labels), auto-discovered from its file; order/home set play order and the boot board. Replaces the single level.py. - judge is now category-agnostic: score_labels takes a Level, and judge() resolves the board from the level_id the client sends (labels/few-shot stay server-side). - engine: empty "" tiles are walkable but wordless; board build factored into buildTiles/buildTargets/loadLevel so boards swap client-side; camera reframes to board size; checked targets persist per board. - nav: hand-drawn ink arrows walk between boards — "next" unlocks after the first target is collected, "back" returns to the previous board. - animal board: 5x5, central start, four ⏎ corners, walkable blanks, ten animals to collect. 25 fast tests pass; verified both boards in-browser with the offline judge. Co-Authored-By:…4a497b2 -
feat(mobile): swipe controls and responsive layout (#13)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureMake the game playable on phones. The board was keyboard-only and assumed a desktop window. Controls: a swipe maps 1:1 onto the existing arrow keys. Pointer Events on the stage read the gesture's dominant axis on release and feed the same DIRS vector through a shared handleDir() (keyboard + swipe), so mid-hop buffering still applies. Touch/pen only — mouse stays on the keyboard. A <28px drag is a tap and is ignored. Layout: 100dvh, touch-action/overscroll locked so swipes don't scroll/zoom; safe-area insets; a compact pass for narrow (portrait) and short (landscape) viewports keeps the HUD on one line and recenters the board into the clear band; tiny phones (<=340px) drop the "targets:" word so the hop counter never clips. Viewport meta is locked from JS (single tag, no pinch/double-tap zoom) and Gradio's wrapper padding is zeroed for a true full-bleed. Verified with Playwright at 320/360/390 portrait + 844x390 landscape: all four swipes hit the correct tile, tap/mouse ignored, keyb…a808a72 -
refactor(ui): replace LLM jargon with friendlier game labels (#12)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signatureMake the game read like a puzzle, not a model-internals demo: - HUD "context window: N/10" -> "hops: N/10"; overflow copy reworded ("last hop\! one more and you're out…", death/stamp "out of hops…") - <bos> tile -> blank home square; <eos> tile -> ⏎ (carriage-return symbol) - "prompt:" strip -> "sentence:", enlarged + bolder, start marker hidden Tokens stay "start"/"⏎" internally (level grid + JS comparisons); only the player-facing text changed. README and level tests updated to match. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>27f427c
-
feat(targets): replace single target with collect-them-all checklist (#11)
Ben Blaker +1: Claude Fable 5Unverified signatureAll four emotions now show as a checklist in the HUD. Winning a round checks off the emotion the judge picked; checks persist across rounds. The judge payload sends the still-unchecked targets, so a repeat emotion stamps "<emotion>, again." instead of a win, and the verdict bars highlight only the targets still left to collect. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>557ba69 -
feat(prompt): move prompt strip from bottom to just under the HUD (#9)
Ben Blaker +1: Claude Fable 5Unverified signatureCo-authored-by: Claude Fable 5 <noreply@anthropic.com>3d6fe70 -
fix(style): keep ink text in browser dark mode (#10)
Ben Blaker +1: Claude Fable 5Unverified signatureGradio's ".prose *" rule sets color: var(--body-text-color) on every element inside the HTML component, and in dark mode that variable flips to near-white while the game's paper background stays light, making the HUD, prompt strip, and verdict card unreadable. Pin --body-text-color to --ink on .sq-root so the rule resolves to ink in both themes. Note: css_template is evaluated inside a JS template literal, so the stylesheet must not contain backticks. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>f33081c
-
feat(prompt): pen-write prompt words instead of boxed chips (#7)
Ben Blaker +1: Claude Fable 5Unverified signature* feat(prompt): pen-write prompt words instead of boxed chips Each landed word is now handwritten into the prompt strip: the ink reveals left-to-right under a travelling pencil nib (clip-path + gsap), in Caveat with no border or background. .sq-chips becomes a wrapping flex row so the unboxed words keep their spacing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(prompt): drop the pencil nib, keep the ink write-out Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>947668c -
feat(game): Web Audio sound effects for hops, keycaps, and verdicts (#5)
Ben Blaker +1: Claude Fable 5Unverified signatureAdd a tiny sample-free Web Audio synth (swept oscillator tones + filtered noise bursts) and wire cues into the game: jump boing, keycap thock on landing, wall bonk, chip pop, context-full warning beeps, dizzy-warble + slide-whistle overflow death, verdict stamp thump with win/lose jingle, and UI button clicks. AudioContext is unlocked on first keydown/pointerdown since most cues fire from gsap timelines outside a user gesture. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>ba42c78 -
refactor(hud): drop token-slot boxes, keep plain N/10 context counter (#4)
Ben Blaker +1: Claude Fable 5Unverified signatureCo-authored-by: Claude Fable 5 <noreply@anthropic.com>09952e9 -
feat(game): 3D keycap board, fleshed character, context-overflow death (#3)
Ben Blaker +1: Claude Opus 4.8 (1M context)Unverified signature- Tiles are now 3D buttons: a shared ExtrudeGeometry of the rounded hand-drawn outline (no square slab) with ink-rimmed cap, hatched paper sides, and a bold bottom silhouette line — one drawn object. Keycaps depress and spring back when landed on. - Board leans away 22 degrees from vertical under a perspective camera viewed slightly from below, so button depth and presses read clearly. - Character is a fleshed doodle (tube limbs, paper-filled bean torso, scarf) with pose frames: idle, think, hop-up/mid/land crouch-tuck-bend, dead-dizzy and dead (X eyes). Billboard now composes inverse-parent rotation and a charTilt spin channel so wobbles/spins render on the tilted board. - HUD reads as an LLM context window: "context window: N/10" counter plus ten token slots that fill with landed words; red warning at 10/10. - Exceeding the context window now KILLS the character (crumple, slide off the board, fade) and shows a "context exceeded!" card without ever calling the ju…45f65df -
feat(game): one-deep input buffer for responsive mashing
Buffered move captured mid-hop and consumed on landing; cleared on submit so it never leaks into the next run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>64bec1e -
feat(game): judging flow, verdict card, reset
server.judge takes a single payload dict (Gradio component server binds multi-arg calls incorrectly), thinking bob + slow hint while judging, sorted probability bars with counting percentages, rubber stamp, error card with retry, and client-side reset. JUDGE_FAKE env enables offline dev. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>5001285 -
feat(game): character, movement, hop animation, HUD
Billboarded doodle with procedural idle/hop/think frames, arrow/WASD input with bounds bonk, GSAP squash-and-stretch hop, prompt chips + context pips. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>18b0808 -
feat(game): hand-drawn board rendering
Canvas-textured tiles with jittered double-stroke ink, Patrick Hand words, dashed <bos>/<eos>, ortho tabletop camera, and a 3fps sketch-boil redraw. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>9a77e8b -
feat(app): gr.HTML shell with templates and server function
Proves the wiring: three.js via ESM dynamic import, GSAP + fonts via head, level passed as component value, judge exposed via server_functions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>e911f6b