Skip to content

Changelog ​

Every release, newest first, built from each package's CHANGELOG when this site is deployed. Releases with nothing but dependency bumps are left out. The bigger releases also get a post in the blog.

ProjectLatest
debuff0.27.0
spryte0.12.0
keiko0.7.0
myaku0.5.1
plonk0.4.0

debuff 0.27.0 ​

2026-11-01

@debuffjp/dev

  • 74dfae9 The dev overlay can be dragged by its header, so it no longer has to sit over the part of the game you're looking at. Its position is remembered across reloads and kept on screen when the window shrinks; double-click the mascot to dock it in its corner again. The position option now sets that corner.

debuff 0.26.2 ​

2026-11-01

@debuffjp/cli

  • 71f585c Automatic papercuts now say which machine they came from (OS, CPU count, memory and Node version) and what was checked: for browser_not_found, whether DEBUFF_BROWSER was set and whether Chrome, Edge and Chromium were missing or failed to start. Still no messages, paths or project names. The notice is shown again before the first report with these.

debuff 0.26.1 ​

2026-11-01

@debuffjp/dev

  • b7dd10e - The dev overlay's copy buttons work on a game opened over the LAN or Tailscale (debuff dev --host). That's plain HTTP, where navigator.clipboard doesn't exist, and the buttons silently did nothing. - debuff scenario: a game that throws while loading now fails that scenario with the error, instead of a 15 s timeout reported as an unexpected error. A game that loads but has no dev hooks gets the dev_hooks_missing error and its fix. A dev server whose config hides its local URL falls back to the network URL or its port.

spryte 0.12.0 ​

2026-11-01

  • 3faf45c New mesh(draw, frame, vertices, uvs, indices?, options?): draw an image stretched over triangles you place, for trails, ropes, waving flags and bent sprites. UVs are in source pixels from the frame's top-left, so atlas frames work. On WebGL2 meshes join the shape batch (one draw call with the shapes and sprites around them), and a two-triangle mesh over a frame matches draw.sprite pixel for pixel. Canvas 2D clips and draws each triangle, which is slower and can show faint lines along shared edges. It's a separate import: 1.6 kB brotli for apps that use it (0.1 kB if they already draw shapes), 0.08 kB for apps that don't.
  • 55660ca New mipmaps option for createRenderer: build mip levels for every uploaded image, so smooth images drawn well below their size (zoomed-out maps, charts, thumbnails) stay steady instead of shimmering. Off by default; costs a third more GPU memory per image. Render targets, borrowed host textures and smooth: false draws are unaffected.

debuff 0.26.0 ​

2026-10-31

@debuffjp/core

  • 554f22f smoothMotion(game, () => [player, enemies, shots]) draws moving objects between fixed steps: smooth.x(o) and smooth.y(o) give the position blended by the render alpha, smooth.snap(o) after a teleport. It saves positions before every step, so objects need no registering or clean-up, and it only reads them, so replays and tests are unaffected. It replaces the prevX/prevY bookkeeping and lerp that games wrote themselves. While the game is frozen (game.freeze()), render and the render hooks now get alpha 1 and the camera draws where it is. They used to get the leftover time, so on displays faster than 60 Hz the camera, and anything a game blended by alpha, jumped back and forth on every frame of a hitstop.
  • 0228a0a SpatialHash answers "what's near this" without checking every pair: rebuild(items) once per step, then near(x, y, radius, visit) for every item within the radius (return true to stop) or nearest(x, y, radius, accept?). Results come in a fixed order for deterministic replays, distances are exact, and after the first steps it allocates nothing; cells that stay empty for a whole fill are released. On a crowd of 500 to 2,000 enemies each querying its neighbours it measured 4 to 15 times faster than checking every pair, and level with the grid overbuffed wrote for itself.

@debuffjp/dev

  • 4df1648 Clips and social-size stills for devlogs, store pages and press kits, all on the developer side. - The dev panel has rec and clip: rec keeps the last 15 s of the game, clip saves them as a video in .debuff/captures/ (MP4 where the browser can record it, else WebM). The dev hooks have clips(on) and saveClip(name) for the same. - capture("social") letterboxes the frame into 1200×675, the size X and Bluesky show for post images and link cards, scaled by whole pixels so pixel art stays crisp. capture([w, h]) takes any size. - MCP: game_clip (live mode: start, save, stop), social: true on game_capture, and a devlog-post prompt that captures a still and a clip and drafts short posts for the human to send. Agents never post. - debuff dev writes captures through a new /__debuff/capture endpoint, behind the same own-origin guard as the others. - debuff ship press adds stills and clips from .debuff/captures/ to the kit and flags WebM clips, which X and most stores don't take. Nothing is added to games: templates and builds are unchanged.

@debuffjp/input

  • 2e60761 TouchControls puts a stick and buttons on screen for phones and tablets, feeding the same actions as keys and pads: bind TouchStickLeft, TouchStickRight, TouchStickUp, TouchStickDown and Touch<name> (TouchA) next to ArrowLeft and PadA. The stick is fixed or floating (centred where the thumb lands in a zone), analog through touch.stick.x and y, with a deadzone for the directions. Each contact keeps the control it started on until it lifts, a tap between two steps still counts, and positions can count from the right and bottom edges. They show from the first touch, draw() gives plain circles, and the state is there to draw your own. Tests drive them with pointer.touchStart / touchMove / touchEnd. Input has a virtual map for sources that code sets each step, which TouchControls uses.

@debuffjp/share

  • 4df1648 postIntent({ text, url }, "x" | "bluesky") returns a link that opens the X or Bluesky composer with a post drafted. Text is cut to fit with "…": 280 weighted characters on X (CJK and emoji count 2, a link 23) and 300 graphemes on Bluesky. Nothing is posted, and it's only there if you call it. xWeight(text) gives X's weighted length.

@debuffjp/cli

  • b59829f Fixes from a first-run test, where fresh agents went from debuff.jp to a playable game: - debuff scenario says when it wrote new baselines (saved without a check, so look at them before committing), and after a full run lists baselines no scenario takes any more, such as a template's after you replace its scenario. - Renaming a save key on a game nobody has played yet: the build error now says --accept-saves is safe then. - The launch.url warning says where to set it (launch: { url } in debuff.config.ts). - debuff test outside a project says there's no package.json, instead of "No tests yet". - debuff fuzz prints a line after each run, so its default three minutes aren't silent. - npm create debuff's next steps describe npm test as logic tests then screenshot scenarios, which is what it runs. - The setup skill says which of the two MCP servers to use and that the dev server may pick another port; the templates' agent guides say how long fuzzing takes; the CLI README warns that long scenario waits are slow.

debuff 0.25.0 ​

2026-10-31

@debuffjp/core

  • e44fbbc Custom shaders get frameUv(): the position inside the drawn frame, from (0, 0) at the top left to (1, 1) at the bottom right. uv stays in atlas space, so effects shaped to a sprite no longer need the frame's atlas rect passed as a uniform. @debuffjp/core now re-exports the Shader and UniformValue types.

@debuffjp/dev

  • e44fbbc state() has hidden: true while the tab is in the background, where the browser stops animation frames and tick stands still although nothing paused the game. step(n) still advances it.

@debuffjp/cli

  • ee38f62 On Windows, a command that ran the daily update check (or sent telemetry) could finish its work and then abort with a libuv UV_HANDLE_CLOSING assertion and exit code 127, so debuff build && tsc stopped after a good build. The CLI now lets those connections close before it exits.

debuff 0.24.3 ​

2026-10-31

@debuffjp/audio

  • df8a5df Noise sounds now last as long as their envelope. Before this release a noise instrument (plonk) or noise synth (audio.synth) went silent after one second, however long its attack and decay, because the noise buffer was a second long and didn't loop. Wind, rain, surf and other long hisses now play in full.

debuff 0.24.2 ​

2026-10-31

@debuffjp/dev

  • 4d979ac debuff dev reports bugs inside debuff that the dev tools catch as automatic papercuts, the way the CLI already reports repeated setup failures. A report holds the error name and up to three debuff functions (TypeError at spryte.js:Canvas2D.flush, now on ExplainedError.signature), never the message, line numbers or anything from the game, and goes only after it's been seen in two page loads in a week. The papercuts notice now mentions these reports and is shown once more before any are sent. Off with debuff papercuts off, as before.
  • f343dd3 Runtime errors thrown inside spryte, plonk or myaku are now explained as debuff's (debuff-usage or debuff-bug, with a report link) instead of your-code. Before 0.24.2 only @debuffjp/* packages counted, so a bug inside the renderer or the music player was blamed on the game.

create-debuff

  • f343dd3 Agents now make music and sound effects with plonk unless you name another tool, the same way they draw pixel art with tenten. The MCP server's instructions and prompts, every template's AGENTS.md and the agent skills say so. Sound files you provide still play through audio.play.

spryte 0.11.0 ​

2026-10-31

  • b17a196 Static layers: draw.record(fn) keeps the sprites fn draws in a Layer without drawing them, and draw.replay(layer) draws them all again under the current transform. On WebGL2 a replay is one draw call per eight images, with the instances in a GPU buffer made once, so a tilemap or background costs no JavaScript per sprite. It puts the same pixels on screen as drawing the sprites one by one, pixel snapping included at fractional camera offsets and zoom (checked on a real GPU against direct drawing on both backends). draw.alpha applies at replay. On Canvas 2D, under a custom shader or draw.alpha below 1, replay draws the sprites one by one. After a context loss the layer's buffers are rebuilt; layer.dispose() frees them.

spryte 0.10.0 ​

2026-10-31

  • e44fbbc Custom shaders get frameUv(): the position inside the drawn frame, from (0, 0) at the top left to (1, 1) at the bottom right. uv stays in atlas space, so effects shaped to a sprite no longer need the frame's atlas rect passed as a uniform. @debuffjp/core now re-exports the Shader and UniformValue types.

plonk 0.4.0 ​

2026-10-31

  • e44fbbc play() checks the whole song before it starts and throws there, leaving the current song playing. The error names the step, its pattern, the section and the track, and a modifier with no note in front of it (a lone ?50) says so. Before, the error came from the scheduler on every tick and named only the token.

plonk 0.3.2 ​

2026-10-31

  • df8a5df Noise sounds now last as long as their envelope. Before this release a noise instrument (plonk) or noise synth (audio.synth) went silent after one second, however long its attack and decay, because the noise buffer was a second long and didn't loop. Wind, rain, surf and other long hisses now play in full.

debuff 0.24.1 ​

2026-10-30

@debuffjp/cli

  • fbda4db debuff papercut and report_papercut show the board's next step: the entry's workaround, or that it waits for review. Similar matches print their workarounds too.
  • f57baa0 debuff papercut --hit <id> --workaround "…" and report_papercut with hit send the workaround, which fills the papercut's workaround when it has none. Before, it was dropped. --hit says which other flags it ignores, and links point at papercuts.debuff.jp/<id>.

debuff 0.24.0 ​

2026-10-30

@debuffjp/cli

  • 10ad79d Coding agents now hear about new debuff releases. The agent hook runs at session start too. At most once a day, when a newer debuff is out, it tells the agent the version, the changelog, and that npx debuff upgrade moves everything together, with an instruction to tell the human and not upgrade without their go-ahead. It reuses the CLI's daily registry check, and it's off with DEBUFF_UPDATE_CHECK=0 and in CI. debuff dev now prints the daily update hint under its banner. It never exits, so it used to never show the hint. debuff doctor reports hooks from 0.23 as out of date. --fix now rewrites a .claude/settings.json or .codex/hooks.json that holds only debuff's hooks. It still never touches one with your own hooks or settings; in 0.23 it left every existing file alone.
  • 30416ac New: debuff papercut and the MCP tool report_papercut report tooling friction (a slow or flaky tool, a missing API, confusing docs) to the public board at papercuts.debuff.jp. They show close matches first, so a repeat adds a hit with your minutes instead of a duplicate. Nothing is sent unless you run them, and only the text you wrote goes. The CLI and MCP server also report debuff's own papercuts: a setup failure seen twice in a week, or a command or tool call far slower than it should be. They send a fixed title, an error code, minutes and the version, at most once a day per problem. Off with debuff papercuts off, DEBUFF_PAPERCUTS=0 or DEBUFF_TELEMETRY=0, and skipped in CI.

debuff 0.23.0 ​

2026-10-30

@debuffjp/cli

  • 8b4db39 New projects ship an agent hook (.claude/hooks/debuff.mjs, wired up by .claude/settings.json for Claude Code and .codex/hooks.json for Codex) that checks the agent's edits and commands as it makes them: - It asks the human before screenshot baselines (including the MCP run_scenarios with update) or saves change. - It refuses edits to .debuff/, dist/ and node_modules/, a foreground npm run dev, and hand-changed @debuffjp/* versions. - It warns about new Math.random, Date.now and real-timer lines in src/, which break replays and scenarios. - Once per stop, it asks for debuff test when src/ changed since the last run. debuff test, debuff scenario and the MCP run_tests and run_scenarios now record their runs in .debuff/last-test for this. The rules come from the installed CLI (@debuffjp/cli/hook). The hook does nothing before npm install, and DEBUFF_HOOK=0 turns it off. debuff doctor has a new advisory agent-hooks check, and --fix writes the hook files that are missing without touching existing settings. See debuff.jp/docs/agents#hook.
  • 6fe654d New isolation option in debuff.config.ts for games that use SharedArrayBuffer and worker threads. debuff dev sends the cross-origin isolation headers, debuff build writes them to dist/_headers for Cloudflare and Netlify (added to a _headers you ship yourself), debuff doctor warns about files index.html loads from other sites, and debuff ship itch reminds you to tick itch.io's SharedArrayBuffer option. isolation: true uses embed: "require-corp"; { embed: "credentialless" } is easier on third-party files but Safari doesn't support it. See debuff.jp/docs/threads.

spryte 0.9.0 ​

2026-10-30

  • 0403e36 drawElement (experimental): draw a live DOM element (a menu, a text field, rich text) inside the canvas, through spryte's transforms and shaders, with Chrome's HTML-in-Canvas API (Chrome and Edge 157 and later). The element keeps clicks, focus, typing, text selection and screen readers where it's drawn. It returns false, drawing nothing, where the API is missing, so the element stays in the page. canDrawElements() reports support. Guide: https://debuff.jp/spryte/html-in-canvas

spryte 0.8.0 ​

2026-10-30

  • cc3125d New gpuTimer(draw): GPU milliseconds for what you draw between its begin() and end(), to tell a GPU-bound frame from a CPU-bound one. Results arrive a few frames late and never block. ms stays undefined where timing isn't available (Canvas 2D, and browsers without EXT_disjoint_timer_query_webgl2, which in practice means everything but Chromium on desktop), after a GPU timing glitch, and when a host engine or profiler is already timing the frame. Separately imported, so it adds nothing unless used.
  • c3669d4 draw.sprites() takes interleaved data. Set each.stride and positions becomes one record of stride floats per copy, uploaded to the GPU as is; each.scale, rotation, color and frame then take offsets into the record. positions must be a Float32Array and may view a SharedArrayBuffer, so a worker's buffer draws without a per-frame copy. sprites() inside withShader now stays one instanced draw instead of one quad per copy: the effect is compiled against the per-copy program. Only source(uv), color and uv are guaranteed inside effect there. Frames across images still draw one quad per copy.
  • 56749bb Canvas 2D fallback: tinted sprites in more than 256 colours no longer slow to seconds per frame. The cache of recoloured copies used to clear completely at 256 entries, so animated or per-copy colours re-tinted nearly every sprite every frame; it now drops the least recently used entry and holds up to 1,024 per image. Repeating the last tint also skips the cache lookup. Output is unchanged. Measured in headless Chrome: 6,000 copies in 1,000 colours went from 2.2 s to 36 ms per frame, and 60,000 copies in one colour from 212 to about 180 ms.

spryte 0.7.3 ​

2026-10-29

  • d929832 draw.sprite() is faster again on WebGL2: a sprite drawn under the same transform as the one before it now skips straight to writing its instance, instead of re-checking the transform, the texture and the batch. On 2dbench (RTX 5060 laptop, Chrome 155), the mixed scene at 50,000 sprites went from 5.8-6.0 ms to 4.1-5.0 ms and bunny from 5.2-6.0 ms to 4.1-4.7 ms, over two interleaved passes, with the same pixels. Scenes that don't use draw.sprite per sprite don't change. Startup JS grows by about 0.1 kB (brotli).

debuff 0.22.0 ​

2026-10-28

@debuffjp/assets

  • 0d86d43 Asset ids are typed. debuff dev, debuff build and npx debuff assets write .debuff/assets.d.ts, which lists every asset id with its type and preload group, so a misspelt id (assets.json("levle1")), an id of the wrong type or an unknown group fails to compile. New templates include the file in tsconfig.json and run debuff build before tsc. In an existing project, add ".debuff/assets.d.ts" to include and run debuff build before tsc; without the file, ids stay plain strings as before. AssetId<T> and AssetGroup are exported for your own signatures.

create-debuff

  • af2583c The debuff agent skill now splits feel and look requests in two. A clear direction ("jump higher", "run faster") gets the change, made through params and measured, with before and after numbers and two alternatives. An open-ended ask ("make it nicer") still gets two or three options before any edit. Before this, the skill told agents to offer options for both.

debuff 0.21.1 ​

2026-10-28

create-debuff

  • cb68bb7 The debuff agent skill now also loads when asked to make the player jump higher or run faster, make the game look nicer, or when npm test, a build or a debuff command fails. A new section near the top tells agents not to edit art or feel until the human picks one of two or three options. Before this, agents in the skill eval read the old line ("propose options and let them choose") and redrew the art anyway.
  • bc9cfa2 The debuff agent skill now loads for everyday work in a debuff project (fixing a bug, tuning feel, adding a mechanic, changing art), not only when starting a new game. Before 0.21.1 its description led with making a new game, and in a skill eval Claude Code never loaded it for those tasks. Run npx debuff doctor --fix to update a project's skills.

debuff 0.21.0 ​

2026-10-28

@debuffjp/cli

  • 6b68789 debuff upgrade moves every debuff package to the latest release together (or --to <version>), keeping each range's style, installs with the project's package manager, then runs the new version's doctor --fix, which rewrites the agent skills for it. --dry-run shows the plan; it never downgrades without --to. debuff doctor now says when a newer debuff is out, and once a day the CLI prints one line on stderr when there is one (never with --json; off in CI and with DEBUFF_UPDATE_CHECK=0).
  • dbb1861 debuff doctor gives the fix (npm install) when debuff.config.ts fails only because nothing is installed yet, and doctor --fix and upgrade no longer print a Node deprecation warning (DEP0190) while installing on Windows.
  • 024d1b1 New projects start as a git repository with one first commit, so every change an agent makes shows as a diff to review or undo. --no-git (also on debuff create) turns it off; it's skipped when git isn't installed or the folder is already inside a repository, and it never sets a git identity. Projects also get .vscode/tasks.json with tasks for the dev server, tests, build and doctor.
  • 6b1c064 A command with nothing to act on now says so with its own code: scenario in a project with no scenarios and report with no build exit 2 with nothing_to_run and a fix, instead of unexpected.

debuff 0.20.0 ​

2026-10-28

@debuffjp/dev

  • c3f1aef The collapsed dev overlay shrinks to fit its header (buff, badges, fps and the expand button) instead of keeping the full 290 px panel width.

@debuffjp/ui

  • 36b1bee Docs keep what changed: the ui README and JSDoc describe the squish default, debuffFrame and the lime bars as they were before 0.19, and spryte's docs say what 0.6 did before borrowed textures were looked up every frame.

@debuffjp/cli

  • 06e2494 A failure contract for scripts and agents. The exit code says what to do next: 1 a run reported a failure, 2 setup needs fixing (run debuff doctor; doctor itself now exits 2 when a required check fails), 3 bad input. With --json, stdout carries exactly one JSON document, and a command that can't run prints { "error": { "code", "message", "fix", "transient" } } with a stable code. Unknown options are now an error instead of being ignored, and scenario --json no longer prints a log line before its JSON. MCP tool errors carry the same code and fix.
  • 4fb20f1 New projects carry agent skills for their own debuff version: npm create debuff writes the debuff, debuff-setup, keiko and myaku skills into .claude/skills/ (Claude Code) and .agents/skills/ (Codex, Gemini CLI, Cursor, Copilot), each stamped with metadata.debuff-version. debuff doctor flags missing or stale skills, and --fix rewrites them for the installed version, so an agent's instructions match the code it edits after an upgrade.

create-debuff

  • b7f1dda New games keep the canvas hidden until debuff sizes it. Browsers first paint a 300x150 canvas, and its jump to full screen counted as a layout shift (CLS 0.2-0.4 on a desktop window; 0 now).

spryte 0.7.2 ​

2026-10-28

  • 809a84c draw.sprite() is faster on WebGL2: the vertex shader now does each sprite's rotation, scale and anchor maths, which spryte used to do in JavaScript. In 2dbench's mixed scene (rotation, scale, tint and alpha on every sprite), a 50,000-sprite frame went from 7.1 ms to 5.5 ms on an RTX 5060 laptop, with the same pixels. Unrotated pixel art is still rounded to whole pixels on the CPU, so it lands where it did. Sprites and other drawing (rects, text, shapes) still share one batch. The Canvas 2D fallback is unchanged. Startup code is about 0.7 kB larger (brotli).

spryte 0.7.1 ​

2026-10-28

  • 36b1bee Docs keep what changed: the ui README and JSDoc describe the squish default, debuffFrame and the lime bars as they were before 0.19, and spryte's docs say what 0.6 did before borrowed textures were looked up every frame.

debuff 0.19.0 ​

2026-10-27

@debuffjp/core

  • bcc4cb2 New scale: "pixel" mode: a fixed number of CSS pixels per logical unit (pixelSize, default 3, snapped to whole device pixels), with the logical size growing with the container. Bigger windows and monitors show more of the game instead of bigger pixels; width/height are the minimum.
  • 11d5ea1 The "made with debuff" splash switches on and off like an old TV: a dot of light stretches into a line, the line opens into the card with a flash, sun rays turn behind the logo over scanlines, and at the end the picture collapses back to a line and a dot. With reduced motion it fades in and out with still rays.

@debuffjp/ui

  • 31db4e4 The default look is now neutral. Panels, menus and bars come in plain greys with a white focus, 4px corners and a thin border, so a game no longer looks like debuff unless it chooses to. squish, the old default, is a theme you opt into: spread squish into drawPanel or panelAround, and pass squishBar to bar, to keep the previous look. neutralLight suits light games. The focus pill's squash is now the squash option, on in squish and off by default. debuffFrame is deprecated in favour of squish.

debuff 0.18.0 ​

2026-10-27

@debuffjp/cli

  • c7efeba debuff doctor --fix applies the safe fixes it finds: install, dedupe, adding @debuffjp/dev, and writing a missing .mcp.json, in the project's package manager. Commands that fail on setup now end with a pointer to debuff doctor. The MCP server's instructions get a missing space back.

debuff 0.17.0 ​

2026-10-27

@debuffjp/dev

  • 77f659e The luminance dev hook, which the flashes and contrast scenario checks read, now averages every pixel in each cell itself. It used to shrink the frame with drawImage, which point-samples on macOS, so fine detail scrolling past (the topdown template's grass) failed the flash check there and nowhere else.

@debuffjp/cli

  • ea43c61 debuff doctor checks a project's setup in one pass and prints a fix for each problem: Node version, debuff packages on one version, node_modules matching package.json, a nested copy of @debuffjp/core, @debuffjp/dev and TypeScript, debuff.config.ts, the asset pipeline, saves.json, agent files (AGENTS.md, CLAUDE.md, .mcp.json), the browser scenarios use, and a NODE_ENV left set in the shell. --live also starts the dev server and checks the game exposes its dev hooks. It exits 1 when a required check fails, and --json prints every check with an id. The MCP server gains a doctor tool, and new projects' AGENTS.md lists the command.

debuff 0.16.0 ​

2026-10-27

@debuffjp/dev

  • efec916 The dev hooks' pointerDown(x, y, kind?) and tap(x, y, kind?) take what is pressing: "mouse" (the default, as before), "touch" or "pen". The game sees it in pointer.kind. A touch or pen press is contact 1 in pointer.touches and the primary pointer, and a mouse press is only the primary pointer, so a dev drag no longer looks like both a mouse and a touch. Scenarios take { "pointerDown": [x, y, "touch"] } and { "tap": [x, y, "pen"] }, and keiko's game.pointerDown(x, y, "touch") does the same, through the dev hooks or as real touch events.
  • 96bebe8 The right and middle mouse buttons are bindable: "PointerRight" and "PointerMiddle", so an action like right-click remove goes through input.down/pressed/released, scenarios and fuzz like any key. input.pointer.right and input.pointer.middle hold down, pressed and released, including buttons pressed while another is held; pointer.pressButton("right") and releaseButton simulate them. The dev hooks' press("PointerRight"), scenario { "press": "PointerRight" } and debuff fuzz --keys PointerRight press the button, and recorded replays keep it. The primary pointer ("Pointer") is unchanged.
  • 8e669fe Tests and scenarios can press the right mouse button, scroll the wheel and touch with several fingers, all through the same paths as real input. - keiko: game.press("PointerRight") (and "PointerMiddle"), game.wheel(dy, dx?), and game.touchDown(id, x, y), touchMove(id, x, y) and touchUp(id). debuff games are driven through their dev hooks. Other games get real DOM events: mouse button presses (a button pressed while another is held arrives as a pointermove, as in browsers, and the right button fires contextmenu), wheel events, and pointerType: "touch" pointer events with touch events. game.replay() plays the new scenario steps. - Dev hooks: wheel(dy, dx?), touchDown(id, x, y, kind?), touchMove(id, x, y) and touchUp(id). Recorded replays keep wheel scrolls and touches, so a session played on a phone replays exactly. They need @debuffjp/input 0.16 and say so on older versions. - Scenarios: { "wheel": dy } or { "wheel": [dy, dx] }, { "touchDown": [id, x, y] } ([id, x, y, "pen"] for a pen), { "touchMove": [id, x, y] } and { "touchUp": id }.

@debuffjp/input

  • 5874ea3 input.pointer.kind says what made the latest press: "mouse", "touch" or "pen", so games don't need their own pointerdown listener to read pointerType. Contacts in pointer.touches carry a kind too, and pointer.touchesStarted / pointer.touchesEnded list the contacts that started and ended this step, so games don't diff the map themselves. pointer.touchStart(id, x, y, kind?), touchMove and touchEnd simulate contacts through the same path as real ones: the first contact also presses the primary pointer. pointer.press(x, y, kind?) takes an optional kind (default "mouse").
  • 86eba07 input.rebind(action, keys) replaces an action's bindings, for controls menus. It takes a copy of keys and updates input.keys.captured, so newly bound keys stop scrolling the page and keys no longer bound scroll it again; keys the game added to captured itself are left alone. input.bindings is now Input's own copy of the map passed in, so rebinding never changes the caller's object. The lists inside it are still the caller's arrays until rebound, so existing code that edits them in place keeps working.
  • f9eb177 input.pointer.wheel and input.pointer.wheelX: the wheel or trackpad scroll over the canvas this step, in CSS pixels (line and page deltas are converted), reset after every step. pointer.scroll(dy, dx) simulates it. new Input(game, { captureWheel: true }) stops the page scrolling while the wheel is over the game, for wheel zoom in embeds; by default the page still scrolls as before.
  • dda5f57 Keyboard input works after clicking a game embedded in an iframe (game portals such as Wavedash). The pointer's preventDefault on pointerdown stopped the click focusing the frame, so keys went nowhere until the player clicked outside the game. A press now calls window.focus() when the document doesn't have focus.
  • fbd1160 A press is no longer dropped when setPointerCapture throws. Synthetic pointer events and unknown pointer ids make it throw NotFoundError, which stopped the press from registering. The pointer now tries to capture and carries on if it can't.

@debuffjp/cli

  • e17b3df debuff fuzz --keys accepts the gestures Wheel (a random scroll) and Touch (a finger tapping or dragging somewhere on the game), so touch and wheel games can be fuzzed: debuff fuzz --keys Touch,Wheel. They're opt-in; the default keys are unchanged.
  • 068ed32 launch.video in debuff.config.ts: point it at a short looping MP4 and debuff build copies it to dist/preview.mp4 and adds og:video tags, size included, so Discord and other apps that play link previews show the clip.
  • b87c265 Scenario fixes in debuff scenario and keiko's game.replay(): - { "tap": "KeyT" } taps the key for one step, as keiko's game.tap("KeyT") does. Before, debuff scenario silently spread the string into tap(x, y), and keiko pressed the wrong key. The schema accepts a key code. A tap that is neither a code nor a point fails with an explanation. - { "expect": { "state.x": 0 } } (and the same in until) fails with state.x: expected a comparison object like { "eq": 0 }, got 0, instead of a TypeError or a silent pass. An object with none of eq, gt, gte, lt, lte and approx is reported the same way.

@debuffjp/mcp

  • fead3cd game_input gains wheel (dy, dx), touch_down / touch_move / touch_up (by contact id, with kind: "pen" for a pen), and a kind on tap and pointer_down.

spryte 0.7.0 ​

2026-10-27

  • a5d8af2 spryte/pixi: draw with spryte inside a PixiJS v8 app through a RenderContainer, in Pixi's scene order, under its masks, filters and render textures, on Pixi's own WebGL2 context. Images Pixi has uploaded draw from Pixi's GPU textures and stay marked as used for Pixi's texture GC. Borrowed textures are now looked up again every frame, so a host that frees or re-creates one is never drawn stale.

keiko 0.7.0 ​

2026-10-27

  • efec916 The dev hooks' pointerDown(x, y, kind?) and tap(x, y, kind?) take what is pressing: "mouse" (the default, as before), "touch" or "pen". The game sees it in pointer.kind. A touch or pen press is contact 1 in pointer.touches and the primary pointer, and a mouse press is only the primary pointer, so a dev drag no longer looks like both a mouse and a touch. Scenarios take { "pointerDown": [x, y, "touch"] } and { "tap": [x, y, "pen"] }, and keiko's game.pointerDown(x, y, "touch") does the same, through the dev hooks or as real touch events.
  • 8e669fe Tests and scenarios can press the right mouse button, scroll the wheel and touch with several fingers, all through the same paths as real input. - keiko: game.press("PointerRight") (and "PointerMiddle"), game.wheel(dy, dx?), and game.touchDown(id, x, y), touchMove(id, x, y) and touchUp(id). debuff games are driven through their dev hooks. Other games get real DOM events: mouse button presses (a button pressed while another is held arrives as a pointermove, as in browsers, and the right button fires contextmenu), wheel events, and pointerType: "touch" pointer events with touch events. game.replay() plays the new scenario steps. - Dev hooks: wheel(dy, dx?), touchDown(id, x, y, kind?), touchMove(id, x, y) and touchUp(id). Recorded replays keep wheel scrolls and touches, so a session played on a phone replays exactly. They need @debuffjp/input 0.16 and say so on older versions. - Scenarios: { "wheel": dy } or { "wheel": [dy, dx] }, { "touchDown": [id, x, y] } ([id, x, y, "pen"] for a pen), { "touchMove": [id, x, y] } and { "touchUp": id }.
  • 812b958 Without debuff, touchDown(id, ...) now sends pointerId id + 2, so a finger never shares the mouse's pointerId 1. Touch events keep the id you passed.
  • 70a00c9 npm homepage links to the package's page on debuff.jp.
  • b87c265 Scenario fixes in debuff scenario and keiko's game.replay(): - { "tap": "KeyT" } taps the key for one step, as keiko's game.tap("KeyT") does. Before, debuff scenario silently spread the string into tap(x, y), and keiko pressed the wrong key. The schema accepts a key code. A tap that is neither a code nor a point fails with an explanation. - { "expect": { "state.x": 0 } } (and the same in until) fails with state.x: expected a comparison object like { "eq": 0 }, got 0, instead of a TypeError or a silent pass. An object with none of eq, gt, gte, lt, lte and approx is reported the same way.

spryte 0.6.0 ​

2026-10-27

  • 209fc38 spryteExtern draws images Phaser already loaded from Phaser's own GPU textures, so a texture shared by both is no longer uploaded twice. createSharedRenderer takes a borrow option that lets any host engine lend its textures the same way.

spryte 0.5.1 ​

2026-10-27

  • 70a00c9 npm homepage links to the package's page on debuff.jp.

myaku 0.5.1 ​

2026-10-27

  • 70a00c9 npm homepage links to the package's page on debuff.jp.

plonk 0.3.1 ​

2026-10-27

  • 70a00c9 npm homepage links to the package's page on debuff.jp.

debuff 0.15.0 ​

2026-10-23

@debuffjp/dev

  • 7dbe21a The dev overlay stays out of automated runs and out of game state. In automated browsers (navigator.webdriver, as under scenario runs and agent harnesses) it now starts hidden, so it no longer sits in every screenshot or catches taps meant for the game's corner; the toggle key still shows it, and devOverlay({ open: true }) keeps it visible. state().plugins leaves out debuff's own tools (providers named dev-*, such as the overlay's panel state), so harnesses that diff state see only the game.

create-debuff

  • bc01ad0 The template picker groups templates into base templates (starting points for your own game) and examples (finished small games). Four new base templates come with pixel art drawn in tenten: topdown and isometric (a tileset, buff with walk animations, pickups), sidescroller (run, jump, one-way planks) and pointclick (rooms, hotspots, an inventory, speech). Squish is listed as "Match 3". Templates declare kind in template.json.
  • 7dbe21a The templates' agent guide says what inspect() is for: it is all that tests, scenarios, fuzzing, replay diffs and agents can see of the game, so report what a player reacts to as lists of positions, not counts, cap large lists, and name the camera or board transform plainly. It also points games whose logic awaits something at await game.run() instead of wait().

debuff 0.14.0 ​

2026-10-23

@debuffjp/assets

  • 7bf806f New: reload(id) re-fetches an asset in place and onChange(id, listener) hears about it; debuff dev uses them when a level or data file changes on disk. One weakly held listener serves every loader, so loaders a game drops are collected. A reload that finishes before an earlier, slower fetch keeps its newer value, and every onChange listener runs even when one throws (the first error is reported after).

@debuffjp/core

  • eb62bc0 Plugin API additions from building the first plugins outside this repo: - game.hooks.renderScene((draw, render) => ...) wraps beforeRender and the scene's render, so a plugin can draw the world through a render target, filter() or shader. afterRender stays outside it. - game.emit called from a gameEvent listener now waits until the current event has reached every listener. Before, later listeners saw the second event first. - @debuffjp/core/testing exports createTestGame(), a Game that runs under happy-dom or jsdom for unit-testing plugins.

@debuffjp/dev

  • ca6ba6a state() adds camera (centre, zoom and view size in world units) so external harnesses can place inspect() rows in the view.

@debuffjp/cli

  • 7bf806f debuff dev re-fetches changed level and data files in place instead of reloading the page, when the compiled manifest serves the file as json or text and a loader holds it; anything else (images, sounds, added or removed files, data no loader has) still reloads the page. Asset recompiles after a change run one at a time, so the served manifest is always the latest.
  • 3e03829 .tt sprites drawn with tenten, the pixel-art MCP server for agents, become PNGs and sprite sheets in assets/ with no configuration, like .aseprite files do with aseprite(). tenten is found in $TENTEN or the project, and otherwise fetched with npx -y tenten the first time a .tt file appears; tenten({ command }) overrides that. New projects' .mcp.json lists the tenten server next to debuff's, and the template agent guides, the debuff skill and the MCP server tell agents to draw pixel art with tenten unless the human names another tool.

spryte 0.5.0 ​

2026-10-23

  • 7838cf8 draw.sprites takes a frame per copy: pass a Frame[] as the first argument and index it with each.frame. A belt of different items or a swarm at different animation phases is one draw call instead of one call per frame. Frames on one image draw instanced; frames across images fall back to one quad per copy. The Canvas 2D fallback and withShader take the same path as before.

spryte 0.4.3 ​

2026-10-23

  • 927bbd0 Ten fixes from a review of the whole package: - roundRect lost its last corner at radii of about 18 device pixels or more: the point buffer was half the size it needed. - The text cache for strings that get their own canvas (the Canvas 2D fallback, and tall strings on WebGL2) grew without bound while text kept changing. It now holds at least 256 strings, plus whatever is on screen. - An image drawn before it finished loading stayed invisible: its 0×0 size and empty texture were cached for good. Both are now read again once it has loaded. - draw.invalidate(src) also forgets the source's size, so a resized canvas draws at its new size. - mask() and filter() inside draw.clip() on Canvas 2D leaked the clip's save() every frame, and on WebGL2 a filter's first pass was cleared only inside the clip, so its blur sampled stale pixels at the edges. - into(), mask() and filter() nested inside a mask or filter at a render scale other than 1 drew at the wrong scale: the outer size was taken in pixels instead of logical units. - into() with a translucent clear colour on WebGL2 cleared with straight alpha instead of premultiplied, so the colour came out too bright and differed from Canvas 2D. - A target the GPU refuses (too large, or an incomplete framebuffer) no longer leaves into()'s transform, clips, alpha and render scale changed. - Shared mode: a render target first drawn as an image (never drawn into) rebound the canvas instead of the host's framebuffer for the rest of the frame.

spryte 0.4.2 ​

2026-10-22

  • dc56b3d Republish of the shared-mode context-loss fix: 0.4.1 was published from a build made before it, so its createSharedRenderer still misses a loss restored between two frames. 0.4.2 carries the fix.

spryte 0.4.1 ​

2026-10-22

  • 210ec36 Shared mode (createSharedRenderer, spryte/phaser): recover from a WebGL context loss that is restored before spryte's next frame. spryte polled isContextLost() at begin(), so a loss and restore between two frames went unnoticed and it kept using objects of the dead context. It now notes both events (without claiming them from the host) and rebuilds at the next begin().

spryte 0.4.0 ​

2026-10-22

  • 503e0df spryte inside another engine. createSharedRenderer(gl) draws on a WebGL2 context the host owns, inside its frame and draw order: into the host's framebuffer, under its scissor and stencil, leaving its texture filtering alone. spryte/phaser wraps it for Phaser 4: spryteExtern(extern, render) draws in an Extern's slot with Phaser's camera applied, and createPhaserContext() gives Phaser a WebGL2 context with its shaders translated to GLSL ES 3.00 (Phaser 4.2.1's Gradient, noise normal maps and smoothPixelArt don't compile on WebGL2 otherwise). Also draw.transform(a, b, c, d, e, f) multiplies a matrix onto the transform, and draw.begin(null, ...) draws over the previous frame instead of clearing.

plonk 0.3.0 ​

2026-10-22

  • ec15658 player.step: where the music is now, in steps from the top of the song (fractional, counting up across loops, following tempo(); -1 when stopped). Read it each frame to move things in time with the music.

debuff 0.13.0 ​

2026-10-21

@debuffjp/audio

  • 8840cfb New package, plonk: game music made in code. Songs are small data (instruments and patterns of notes), synthesized live with Web Audio: no audio files, about 2 kB brotli, any engine. Tempo changes without jumps, per-track layers that fade in and out, a tempo-synced echo, and notes() to see a song as data. @debuffjp/audio gains audio.bus(name), so plonk (or anything else) can play through a game's music bus and share its mute.

debuff 0.12.0 ​

2026-10-21

@debuffjp/core

  • c6311a2 Agent tooling from building gloop: - MCP: new game_command tool runs a game's dev command by name with JSON args. game_start takes viewport: [w, h] (headless) and reducedMotion to force it on or off. game_frames takes summary (percentiles and the 3 slowest frames), game_step takes quiet (no inspect state), and game_capture takes path to save the PNG in the project. game_profile in headless mode now says profiling needs live mode instead of blaming requestAnimationFrame. - dev: state() reports the logical width/height, and reducedMotion and reducedMotionForced; new reducedMotion(value | null) hook forces it. - core: Random#state gets and sets the generator's state, for undo and replays. - keiko: toBeLessThan / toBeGreaterThan matchers; docs say -t is a case-insensitive substring match and point async game logic at run() / settle() instead of wait(). - MCP: headless errors are now just the message (no page.evaluate: prefix or stack). - MCP, scenarios and dev: calling a hook an older @debuffjp/dev doesn't have now says to update it, instead of "is not a function".
  • 7a1135e New scale: "expand" mode for responsive games. Like fit, but instead of letterboxing, the logical size grows along the spare axis to fill the container: width/height become the minimum, game.width/game.height follow the container, and game.hooks.resize(fn) fires when they change. One game can be portrait on phones and landscape on a desktop portal.
  • c18bf4b In dev builds, Game now warns when it starts with no active scene ("call game.go(id) first"), or when scenes were added but game.start() was never called, instead of silently drawing only the background. Dev-only checks are also written so bundlers fully drop them from production builds.

@debuffjp/input

  • 08e3a38 Gamepad polling no longer crashes a game embedded in a frame whose permissions policy blocks the Gamepad API (chat app sandboxes, some embeds): getGamepads() throwing now counts as no pad.

debuff 0.11.0 ​

2026-10-21

@debuffjp/core

  • 4074fea Saves survive game updates (ADR 0014). Storage uses a saved field only if its JSON type matches the default's, so a save from an older version of the game can't break it (the original is kept once under <key>:backup). new Storage(key, defaults, { version, migrate }) upgrades older saves once. debuff build finds every save, compares it with saves.json (committed, like a lockfile), fails if a save key disappears and warns when a field's type changes without a version bump; --accept-saves records intended changes. debuff inspect and the MCP build tool include saves, and the templates ship a saves.json.

debuff 0.10.0 ​

2026-10-21

@debuffjp/dev

  • ea6a86a Type errors while you play: set typecheck: true in debuff.config.ts and debuff dev type-checks with your project's own tsc after every save. Type errors show as a badge and cards in the dev overlay (the game keeps running), in window.__DEBUFF_DEV__.typeErrors(), and to agents through the new typecheck MCP tool, which also works without a browser.

create-debuff

  • 698f6f0 The basic template turns on typecheck in its debuff.config.ts, so new games see type errors in the dev overlay.

keiko 0.6.0 ​

2026-10-21

  • c6311a2 Agent tooling from building gloop: - MCP: new game_command tool runs a game's dev command by name with JSON args. game_start takes viewport: [w, h] (headless) and reducedMotion to force it on or off. game_frames takes summary (percentiles and the 3 slowest frames), game_step takes quiet (no inspect state), and game_capture takes path to save the PNG in the project. game_profile in headless mode now says profiling needs live mode instead of blaming requestAnimationFrame. - dev: state() reports the logical width/height, and reducedMotion and reducedMotionForced; new reducedMotion(value | null) hook forces it. - core: Random#state gets and sets the generator's state, for undo and replays. - keiko: toBeLessThan / toBeGreaterThan matchers; docs say -t is a case-insensitive substring match and point async game logic at run() / settle() instead of wait(). - MCP: headless errors are now just the message (no page.evaluate: prefix or stack). - MCP, scenarios and dev: calling a hook an older @debuffjp/dev doesn't have now says to update it, instead of "is not a function".

spryte 0.3.0 ​

2026-10-21

  • cff2aef New blend modes: multiply, screen and erase, alongside normal and add. WebGL2 and Canvas 2D draw them identically over opaque pixels (checked on a real GPU).
  • a5e021b Filters: filter(draw, filters, fn) draws fn through a chain of ready-made effects: blur(), grayscale(), sepia(), saturate(), hueRotate(), brightness(), contrast(), invert() and colorMatrix(). Each is tree-shakeable. On Canvas 2D the named filters use the browser's CSS filters, which WebGL2 matches to within 2 levels per channel.
  • a788f55 mask(draw, shape, fn): keep what fn draws only where shape draws. The mask can be anything you can draw (sprites with soft alpha, shapes, text), it nests, and it works inside render targets. A rectangle mask is pixel-identical to clip() on both backends.
  • d7ec5ea nineSlice(draw, …) for UI panels that stretch without stretching their corners, and tiled(draw, …) for repeating fills (scrolling backgrounds, floors) that work on frames inside an atlas. Both are plain quads, so they batch with sprites, run through custom shaders and match on both backends.
  • 6dff019 Shapes, masks, filters, nineSlice and tiled are standalone functions that take the Draw (roundRect(draw, …), mask(draw, shape, fn)), so apps only ship what they use. spryte's startup JS is 11.3 kB brotli without them (a minimal app, bench/size).
  • 1979a2e Vector shapes: polygon (concave is fine), polyline (strokes with mitred joins), roundRect (filled or outlined) and ring. On WebGL2 they're tessellated on the CPU and antialiased with a one-pixel feathered edge, so they match Canvas 2D's antialiasing without MSAA, and they batch in draw order with sprites. Custom shaders (withShader) don't apply to shapes.
  • b563010 blur() takes half the texture samples (two texels per tap through linear filtering) and reaches twice as far: blurs up to about 40 device pixels now match CSS blur(), instead of being cut short past 20.
  • 885d45d Canvas 2D fallback: browsers whose Canvas 2D ignores ctx.filter (Safari before 18) now warn once and draw unfiltered, detected with a one-pixel probe. GPU checks run in Firefox and WebKit too (SPRYTE_BROWSER=firefox|webkit).
  • 5e31eeb Shapes batch faster: indexed geometry and a fan for convex shapes (5,000 round rects: 39 ms to 17 ms on an AMD 780M laptop), and sprites drawn between shapes join the shape batch, so UI that alternates panels and icons is one draw call instead of one per switch (5,000 pairs: 50 ms to 18 ms).

spryte 0.2.3 ​

2026-10-21

  • 30ff49d draw.circle stays crisp at large sizes. It used to stretch one 64px texture to every radius, so big circles (and the rounded corners built from them) blurred, especially on high-DPI screens. It now picks a 64, 256 or 1024px texture by the circle's size on screen.

spryte 0.2.2 ​

2026-10-21

  • 2e71515 README: particle frame-time chart and the raycaster result from 2dbench. Benchmarks doc: results across devices.
  • 33d6a29 Text that changes often is much faster on WebGL2. draw.text with a CSS font now packs strings into shared atlas pages and uploads only each new string's rectangle, instead of creating a texture per string, so labels batch into one draw call. In 2dbench's text scene (1,000 labels, each changing every 8 frames) a frame drops from 10.4 ms to 2.7 ms on an RTX 5060 laptop; at 5,000 labels spryte (5.3 ms) is now ahead of Canvas 2D (9.8 ms) and PixiJS BitmapText (22.9 ms). Pixels are unchanged, and the Canvas 2D fallback keeps its per-string cache.

plonk 0.2.0 ​

2026-10-21

  • b44af49 plonk learns to sound good, and to check that it does. A master bus (gentle compressor, then a limiter: the mix can't clip), a seeded reverb, and stereo pan per instrument. Musical vocabulary: song key and transpose, scale degrees (^3@5), chord names (Am7@3), accents (!), ghost notes (_), chances (?, ?30, seeded), and tracks that take turns between variants (fills). player.sfx() plays a sound effect by scale degree in the song's key, optionally on the next step, beat or bar. And render(), analyse() and mix() measure music without ears: loudness (ITU-R BS.1770 LUFS), peaks and clipping, spectrum balance, and each track's level against the mix, with warnings in plain words. notes() caches each song's layout (60x faster).
  • 8840cfb New package, plonk: game music made in code. Songs are small data (instruments and patterns of notes), synthesized live with Web Audio: no audio files, about 2 kB brotli, any engine. Tempo changes without jumps, per-track layers that fade in and out, a tempo-synced echo, and notes() to see a song as data. @debuffjp/audio gains audio.bus(name), so plonk (or anything else) can play through a game's music bus and share its mute.

spryte 0.2.1 ​

2026-10-20

  • 984a0eb README: logo, what spryte is for beyond games, links to the guides and the PixiJS migration guide, and where to get help.

spryte 0.2.0 ​

2026-10-20

  • 1ca1155 createRenderer(canvas, { transparent: true }) makes a see-through canvas: clear with an alpha colour ("#0000") to draw over the page or another renderer, such as particles over a PixiJS scene.
  • 68dec64 Canvas 2D fallback: tint and flash on partly transparent pixels now match WebGL2 (they bled the raw tint colour in before). Tinted or flashed frames are recoloured once per source, frame, colour and flash and cached.
  • 3d1f9e2 No more flicker on dual-GPU Windows laptops: spryte no longer asks for a desynchronized (low-latency) canvas, which Chrome presents unreliably there.
  • 6bc9e5d draw.text: the text cache no longer thrashes when a frame shows more than 256 distinct strings. It used to evict and re-rasterise strings still on screen every frame (155 ms a frame for 540 labels in 2dbench, 709 ms for 2,000). Now it never evicts a string drawn this frame or the last and grows to fit: 5.4 ms and 12.3 ms. Recovery after a lost WebGL context drops from 737 ms to 93 ms.

spryte 0.1.2 ​

2026-10-20

  • 3a328dd npm description and README lead with the measured result and link 2dbench.

debuff 0.9.2 ​

2026-10-19

@debuffjp/assets

  • 9ee5b42 Only the left mouse button presses Pointer, keys release when focus leaves the keyboard target, and asset URLs carry a content hash so a redeploy can't mix old and new atlases.

@debuffjp/dev

  • cc424e9 Gamepad input is now recorded, so rewind, the reload that keeps your place, and replays saved as scenarios reproduce pad sessions. Scenarios gain a pad step.

@debuffjp/cli

  • c2a9cfa The dev server's agent endpoints now refuse requests from other origins and POSTs that aren't JSON, so a website you visit can no longer write scenarios or drive your game.
  • 7c2ee14 Failed asset imports are no longer cached, malformed asset URLs get a 400 instead of hanging, sibling folders like assets-src/ no longer trigger reloads, and image preloads match the versioned asset URLs.

debuff 0.9.0 ​

2026-10-19

@debuffjp/core

  • 67d406b The renderer now lives in its own package, spryte, which core depends on and re-exports. Game code is unchanged. Also: texture coordinates are highp (fixes off-by-a-texel sampling in large atlases on some mobile GPUs), drawing one image both smooth and pixelated no longer re-uploads it, and game.dispose() frees every GPU object. New: draw.font sets the default font for draw.text (debuff sans in games).

@debuffjp/dev

  • d159eef The "squish" look everywhere: - ui: panels and menus that set no frame of their own get round corners, no outline, light labels, lime focus and a jelly highlight that squashes when focus moves (debuffFrame is this look; FrameStyle.radius is new). Styles that set their own borders or fill are untouched. - dev panel: soft rounded panel, pill buttons and tabs, controls that squash on press, round toggles and sliders. - CLI: buff is flat with dot eyes, and squashes when a command succeeds. - basic template: buff is a flat jelly block with pill eyes, little feet, an idle jiggle and a hop on pop.

@debuffjp/share

  • 61798e2 New package: @debuffjp/share, with rolling canvas clips (ClipRecorder), the share sheet with a clipboard fallback (share), "beat my run" links (challengeLink, readChallenge), screenshot and download.

@debuffjp/cli

  • 61798e2 debuff build now adds link-preview tags (Open Graph and Twitter), a web app manifest and a preview image (your first scenario baseline) to dist/, configured by launch in debuff.config.ts. New debuff ship itch|wavedash|press gets a build ready for a store: an itch.io zip with limit checks, Wavedash config and Wavedash.init() checks, or a press kit with a factsheet and screenshots. It never uploads anything.

debuff 0.8.0 ​

2026-10-19

@debuffjp/juice

  • 5504289 - juice: squash() only replaces scale tweens, so a move or fade already running on the same object keeps going (it used to stop every tween on its target). tweens.kill(target, props?) can stop just some properties, and tweens.has(target, prop?) says whether something is still animating. - Scenarios and fuzz: set the browser viewport with scenarios: { viewport: [360, 640] } in debuff.config.ts, or "viewport" in a scenario file, so portrait games are tested at their own shape instead of shrunk into 960×540.

@debuffjp/cli

  • 0f12c4b debuff build registers the project anonymously with debuff.jp (a random project id, the package name and the CLI version; nothing about code, paths or players), so we can find games made with debuff. A one-time notice says so. Turn it off with debuff telemetry off or DEBUFF_TELEMETRY=0; it is off in CI. Details: https://debuff.jp/docs/telemetry
  • 77fe8c6 Fix: a project with a debuff.config.ts built in development mode (loading the config through Vite set NODE_ENV=development), so debuff build failed its dev-leak check or, worse, could ship the dev tools. Loading the config now leaves NODE_ENV as it was.

create-debuff

  • 2a1f9d3 New template: squish, a squishy match-3 for touch and mouse (swaps, cascades, combos, squash-and-stretch juice, drawn in code with no art files). npm create debuff@latest my-game -- --template squish.

debuff 0.7.1 ​

2026-10-19

@debuffjp/core

  • 1d8036c - Camera: a followed sprite no longer shimmies a pixel while it moves. Camera.view() stops rounding its offset, and Draw snaps each sprite once, after the camera. - Scenario contrast: samples one cell per logical pixel (up to 1024 a side) instead of averaging regions wider than 256 px, and compares the background with the text on it, so short labels in big boxes and 1-pixel pixel-font strokes are judged correctly. - debuff fuzz: a loop now needs the scene, or two state fields, flipping together; a single counter rising and falling (bullets, particles) no longer counts. --ignore <paths> skips noisy fields.

keiko 0.5.1 ​

2026-10-19

  • 1754dac -t/--filter no longer hides test files that fail to load, and a filter that matches no tests now fails. A test that times out can no longer close the next test's page when it calls play() late.
  • 97deedd Commented-out scripts are no longer booted, failures keep their file and line under paths with parentheses, test files outside the project get a clear error, duplicates run once, durations like 1e3ms work, and page accepts backslashes. Needs Node 22.4 or later.

keiko 0.5.0 ​

2026-10-19

  • cfa9fff Start testing an existing game in a minute. npx keiko init writes a first test for the engine your project uses and prints a starter window.__KEIKO__ hook. game.engine reads the active scene and the pressable, on-screen objects straight from Phaser 3 or PixiJS 8, with no hook. await game.tapWhenReady(find) waits for a button to appear and stop moving before tapping it.
  • d4a6c44 Clearer failures: a test.deterministic divergence between runs with different seeds suggests { sameSeed: true } when the game takes its randomness from Math.random(), and an until() or eventually() that times out with the state unchanged since the last pointer tap() says the tap may have missed or landed before its target took input.
  • a9b440f The first game in a run gets 60 s to load instead of 10 s, so a cold Vite dependency pre-bundle no longer fails it.
  • a761975 keiko: new Date() and Date() now run on the virtual clock, not just Date.now(). test.deterministic now gives its second run a different date as well as a different seed, as documented. A pointer press at a new position now moves the pointer there first, as in a browser, so engines that only track the position on moves (KAPLAY) see the right spot. myaku: corrections to the README. Both packages are now checked against real Phaser 3 and 4, PixiJS, KAPLAY, Excalibur and three.js with a local suite (npm run compat).

myaku 0.5.0 ​

2026-10-19

  • f29a0f0 playwright-core is now an optional peer dependency, so using only the in-browser profiler no longer installs it. myaku record users need npm install -D playwright-core; without it, the CLI says so.
  • f739c4a Hidden and throttled frames no longer count against fps, percentiles or hitch rate (reported under paused). Percentiles use nearest rank, so p99 of a short run is no longer always the max. --trace ignores screen gaps after the recording ended, uses the trace's refresh rate when the page couldn't measure one, and clips script time to the frame it blames.
  • d224bda record --trace no longer says hitches went unseen when the trace has no presented frames or ended early (they are now "not covered by the trace"), notes when Chrome's trace buffer filled, and only blames the compositor when it can see canvas GPU work.

myaku 0.4.0 ​

2026-10-19

  • 4dfc682 myaku record --trace records Chrome's own frame pipeline while it plays, and checks myaku against it: presented-frame percentiles next to the rAF ones, each hitch confirmed or flagged as not seen on screen, gaps myaku missed, and unknown hitches named gpu or compositor from evidence. A lone GPU stall on a lightly loaded GPU stays unknown and says it's likely outside the game. --trace-out saves the raw trace for DevTools or Perfetto. Opt-in and Chromium only; nothing changes in the page. The display rate is now measured again every 30 frames, keeping the fastest rate seen, so a slow start (shaders compiling, a heavy first scene) no longer doubles the hitch budget for the whole run. profiler.budgetMs can now be set.
  • c2d6ed6 Frames whose own code overran the budget (say 31 ms of game code) are now hitches even when Chrome stamps the next frame on time. They were invisible before, and too short for Long Animation Frames, which only reports frames over 50 ms.

myaku 0.3.0 ​

2026-10-19

  • d4a6c44 update and render spans for Excalibur (found automatically) and KAPLAY (automatic with its default globals, or adapt(profiler, k)). PixiJS apps are now found even when Pixi runs its devtools hook before the ticker is set up, which left real games like Puzzling Potions and Bubbo Bubbo without spans.
  • a761975 keiko: new Date() and Date() now run on the virtual clock, not just Date.now(). test.deterministic now gives its second run a different date as well as a different seed, as documented. A pointer press at a new position now moves the pointer there first, as in a browser, so engines that only track the position on moves (KAPLAY) see the right spot. myaku: corrections to the README. Both packages are now checked against real Phaser 3 and 4, PixiJS, KAPLAY, Excalibur and three.js with a local suite (npm run compat).
  • cfa9fff Spans that ran but measured 0 ms (the browser rounds performance.now()) now still appear in frames and reports, instead of looking as if the engine was never hooked up.

spryte 0.1.1 ​

2026-10-19

  • 247d5a4 Fixes from an adversarial QA pass: a shader that fails to compile no longer breaks the renderer; draw.sprites clamps count to the arrays it's given (it used to draw nothing on WebGL2 when count was too large); Canvas 2D keeps smoothing correct after a clip; oversized or fractional render targets throw clearly; using a disposed Draw throws. README and benchmark docs now use the interleaved, parity-gated results: 2–3.8× faster than PixiJS 8 like for like (0.1.0's README overstated several cases).

debuff 0.7.0 ​

2026-10-18

@debuffjp/core

  • 4798506 The splash's "made with" line is one scale step smaller.

@debuffjp/dev

  • 4798506 Dev overlay polish: buff alone in the header, rectangle slider handles over whole segments, a pixel speed picker, debuff sans param names, centred labels, the active tab opening into the panel, quieter group headers, outline off-toggles, a –/+ collapse button and an 8px rhythm.

@debuffjp/ui

  • a872480 Frames can have chamfered pixel corners (chamfer) and a hard drop shadow (shadow, shadowOffset), and bar() draws a segmented meter that lights whole, equal cells. debuffFrame is debuff's own panel look, to spread into any frame or menu panel style. The defaults are unchanged.

debuff 0.6.0 ​

2026-10-18

@debuffjp/core

  • 22157f7 draw.text and menus now default to debuff sans, a built-in pixel font (printable ASCII, 5x7 capitals, proportional), instead of the browser's 8px monospace. debuffSans() returns it as a BitmapFont. Pass font: "8px monospace" to keep the old look.
  • 6b0ff8a debuff sans everywhere: the splash's "made with" line and the dev overlay's labels (title, buttons, tabs, chips, param groups) use it. Longer readouts stay monospace for legibility.

@debuffjp/dev

  • 5a34826 The dev overlay has its own look: chamfered panels, keycap buttons that flash on click, segmented sliders, stack-style param groups and status chips. The styles are a small class-based kit, injected once and scoped to the overlay.

debuff 0.5.0 ​

2026-10-18

@debuffjp/core

  • 60fd560 New: rewind. The dev panel's log tab has a timeline slider: drag to any tick of the session and the page reloads, replays your inputs to that tick (without drawing, so it's quick) and pauses there with the game's exact state. You can drag forward again until you play on from the earlier point. Agents can do the same with the MCP game_rewind tool, for example at a note's tick, and games with window.__DEBUFF_DEV__.rewind(tick). save as scenario in the panel writes the session so far to scenarios/<name>.json through debuff dev, with a capture at the end, so a bug you just played becomes a regression test. game.step(steps, false) advances without drawing.
  • 28b3d48 New: assertions on what happens in the game, not on pixels. - game.emit("coin", detail?) marks a game event. The dev tools count events by name in state().events, and __DEBUFF_DEV__.events() lists the latest with their tick and detail. - Scenarios have an until step: { "until": { "events.coin": { "gte": 1 } }, "within": 60 } steps one tick at a time until the conditions hold, and fails with the actual values if they don't. Use it instead of guessing a wait. expect and until read events.<name> as 0 until the first emit. - keiko replays until steps and adds game.emitted("coin"). - New projects' AGENTS.md tells agents to prefer state and event assertions over screenshots, and the platformer emits coin and win, with a collect-coin scenario that uses them.
  • 1f17bac Smaller startup: the splash and the Canvas 2D fallback load on demand, so WebGL2 browsers never download the fallback and games without the splash never download it. The starter's startup code drops from 14.1 to 13.3 kB brotli. debuff build reports code at startup separately from code loaded on demand.
  • b37f2d9 Document turning off the splash for portals: the splash option's docs and new projects' AGENTS.md say Poki requires splash: false, with a link to the new publishing guide.
  • 092b259 The start-up splash shows the new DEBUFF logo on a slanted stripe, punching in and then "boiling" like hand-drawn animation (still for players who prefer reduced motion). The splash no longer opens debuff.jp on click or shows a URL: a click or tap only skips it, so games are safe on portals such as Poki and CrazyGames that forbid links out of the game.
  • a162599 The splash uses the DEBUFF wordmark (white letters, thick black outline) without the swoosh.

@debuffjp/dev

  • c0236d6 New: accessibility checks in scenarios. - { "flashes": 240 } plays 240 steps, samples every frame, and fails on flashing that could trigger photosensitive seizures: more than three flashes a second over a quarter of a 10° field. This approximates WCAG 2.3.1's general flash threshold. - { "contrast": [x, y, w, h], "min": 4.5 } checks the contrast between the light and dark parts of a region, such as HUD text. - "reducedMotion": true runs a scenario with prefers-reduced-motion: reduce. These are screening checks on what the game actually draws, not a certification. The dev hooks gain luminance(cols, rows, region?). keiko keeps time moving through flashes steps and skips contrast, since nothing is drawn headless. The platformer ships an accessibility scenario, and new projects' AGENTS.md asks agents to add these checks for flashing effects and new HUD text.
  • 446c446 New: state-preserving reload. Under debuff dev, a code change no longer sends you back to the start. Just before the page reloads, the dev tools save the session's inputs, and the new code replays them to the tick you were on (without drawing, so it's quick). The game then carries on running. Because the replay runs your new code, a change to how the game plays shows up right where you were. Keys you were holding are let go after the replay. Turn it off with devTools({ input, keepState: false }).

@debuffjp/cli

  • 437f2ac Two fewer dependencies: terminal colours (picocolors) and argument parsing (cac) are now small built-in modules with the same behaviour, including NO_COLOR/FORCE_COLOR and the flag syntax plugin commands use.

@debuffjp/mcp

  • 27dce10 @debuffjp/mcp declares mcpName: "jp.debuff/debuff" for the official MCP Registry.

keiko 0.4.1 ​

2026-10-18

  • 444da77 npx keiko runs debuff projects: it loads debuff's Vite plugin from the project's @debuffjp/cli (or says to install it), and a JSON fetch answered with index.html now says so instead of Unexpected token '<'.
  • a586450 Tests no longer hang forever when a game imports a dependency Vite has not pre-bundled yet. Vite's dependency optimizer now gets real timers, and a boot that never settles fails with an error after its timeout instead of waiting forever.

debuff 0.4.0 ​

2026-10-18

@debuffjp/dev

  • 771ec97 New package: myaku, a frame profiler for web games. It finds the frames players feel drop and explains each one (script with its location, GC, GPU uploads, shader compiles, layout, browser throttling), for any engine. @debuffjp/dev starts it with debuff's update and render as spans and adds profile(); the MCP server adds game_profile, and the explain-stutter prompt uses it.

create-debuff

  • 793eab1 The "Next steps" note mentions npm test when the new project has logic tests.

keiko 0.4.0 ​

2026-10-18

  • 28b3d48 New: assertions on what happens in the game, not on pixels. - game.emit("coin", detail?) marks a game event. The dev tools count events by name in state().events, and __DEBUFF_DEV__.events() lists the latest with their tick and detail. - Scenarios have an until step: { "until": { "events.coin": { "gte": 1 } }, "within": 60 } steps one tick at a time until the conditions hold, and fails with the actual values if they don't. Use it instead of guessing a wait. expect and until read events.<name> as 0 until the first emit. - keiko replays until steps and adds game.emitted("coin"). - New projects' AGENTS.md tells agents to prefer state and event assertions over screenshots, and the platformer emits coin and win, with a collect-coin scenario that uses them.
  • c0236d6 New: accessibility checks in scenarios. - { "flashes": 240 } plays 240 steps, samples every frame, and fails on flashing that could trigger photosensitive seizures: more than three flashes a second over a quarter of a 10° field. This approximates WCAG 2.3.1's general flash threshold. - { "contrast": [x, y, w, h], "min": 4.5 } checks the contrast between the light and dark parts of a region, such as HUD text. - "reducedMotion": true runs a scenario with prefers-reduced-motion: reduce. These are screening checks on what the game actually draws, not a certification. The dev hooks gain luminance(cols, rows, region?). keiko keeps time moving through flashes steps and skips contrast, since nothing is drawn headless. The platformer ships an accessibility scenario, and new projects' AGENTS.md asks agents to add these checks for flashing effects and new HUD text.
  • 437f2ac Two fewer dependencies: terminal colours (picocolors) and argument parsing (cac) are now small built-in modules with the same behaviour, including NO_COLOR/FORCE_COLOR and the flag syntax plugin commands use.

keiko 0.3.1 ​

2026-10-18

  • 27d109a When test.deterministic finds the runs diverging, the error says whether the game was loading in the steps before (a load racing the frames, not game logic) and points to eventually(). onStep hooks receive whether a load was in flight.

myaku 0.2.0 ​

2026-10-18

  • 4cc2ff5 npx myaku record [dist|url]: plays the game in Chrome or Edge with myaku injected (no changes to the game, works on production builds), prints frame-time percentiles and the worst hitches, and fails when --p95, --p99 or --hitches budgets are exceeded. --play drives the game with a Playwright script, --cpu throttles the CPU, --json/--out write the report. Adds profiler.reset().

myaku 0.1.0 ​

2026-10-18

  • c49aac3 Engine spans: update and render for PixiJS 8 and three.js (found automatically through their devtools hooks), Phaser 3 or any global Phaser (with update:<scene> per scene), and adapt(profiler, game) for Phaser 4's ES modules and anything else. Turn off with myaku({ engines: false }).
  • 771ec97 New package: myaku, a frame profiler for web games. It finds the frames players feel drop and explains each one (script with its location, GC, GPU uploads, shader compiles, layout, browser throttling), for any engine. @debuffjp/dev starts it with debuff's update and render as spans and adds profile(); the MCP server adds game_profile, and the explain-stutter prompt uses it.
  • b8f2649 myaku says what it can't see instead of showing a quiet, healthy-looking report: warnings() (also in the report and the overlay) for no frames, no canvas context, span names past the limit, and a heap too coarse to see GC. support.heap now checks that heap readings actually change, and unknown verdicts say when GC can't be ruled out. Span names past 16 are added up under other instead of dropped.

debuff 0.3.0 ​

2026-10-17

@debuffjp/core

  • 1f5fb28 BitmapFont: pixel fonts from a sprite-sheet grid, with widths measured from the glyph pixels. Pass one as font to draw.text, draw.measure and @debuffjp/ui menus. The CLI ships aseprite/new-font.lua, which creates a blank font sheet with the grid set up.
  • 134c84a draw.measure(text, font) returns the box draw.text fills. @debuffjp/ui now lays menus out from measured text: a fixed cursor column, right-aligned value rows with left/right changes, drawPanel (a titled, centred, evenly padded frame), panelAround, and positions snapped to whole pixels. The platformer's pause menu gains a settings screen with a saved volume.
  • d716ede Canvas 2D fallback: tinted textures now get their colour, not just their alpha, so text and bitmap fonts render in the requested colour as on WebGL2. Platformer template: a pause button in the HUD for touch players.

@debuffjp/dev

  • 91f7088 __DEBUFF_DEV__.state() reports step, the seconds per fixed simulation step.

@debuffjp/ui

  • 8529aa9 Menus respond to taps and clicks: a tap selects the row under it, and on a value row the left half lowers the value and the right half raises it. Pass an Input (or anything with pointer) to update.

@debuffjp/cli

  • 91f7088 debuff test runs logic tests (tests/**/*.test.ts) with keiko: the real game in Node with no browser, on a virtual clock, in milliseconds. -t <text> filters by name; --json gives a report for tools.
  • 01c7c17 aseprite() tooling plugin: .aseprite/.ase files in assets/ are exported by Aseprite automatically. Multi-frame files become sprite sheets (assets.frames(id)), name@WxH.aseprite flattens to a grid (for BitmapFont), hidden layers are skipped, and results are cached. Aseprite is found via $ASEPRITE, PATH or the usual install folders, including Steam; if it's missing, the build warns instead of failing.

create-debuff

  • 91f7088 Templates include example logic tests in tests/ and keiko as a dev dependency; npm test runs logic tests, then scenarios. AGENTS.md explains when to use which.

@debuffjp/mcp

  • 91f7088 New run_tests tool runs the project's keiko logic tests; failures carry the tick, scene and state where they broke. The add-mechanic, fix-errors and tune-feel prompts now ask for a logic test.
  • 97c44ec Every tool now declares readOnlyHint or destructiveHint, so MCP clients and directories can tell which tools change things. None of them are destructive.

keiko 0.3.0 ​

2026-10-17

  • 3da761c Runs real open-source Phaser 3 and PixiJS games: browser module resolution and dependency pre-bundling, inline Web Workers, blob: URLs, XHR loads, audio buffers, GLSL reflection and CSS-sized canvases. Adds run() and eventually() for games whose UI flows are async, and a viewport option.

debuff 0.2.0 ​

2026-10-17

@debuffjp/core

  • ab5dcf1 Pixel-art games (pixelArt: true) now draw unrotated sprites on whole pixels. Nearest sampling at fractional positions made sprite edges gain and lose a pixel from frame to frame, which read as flicker on bobbing pickups, particles and eased movement.
  • 78f0a71 The camera now interpolates between simulation steps (camera.renderAlpha, set by Game before each render), so camera motion is smooth on displays faster than 60 Hz instead of jumping every few frames. The templates also interpolate their moving sprites using the alpha passed to render.
  • 2eccd11 Splash redesign: buff is drawn from the same pixel art as the CLI, the icon and text form one centred stack, the wordmark uses Outfit when the player has it installed, and debuff.jp is shown at the bottom.
  • 035dfd9 The splash link now opens https://debuff.jp/?ref=made-with. The tag lets the debuff site count visits that come from games built with debuff. It carries no player data.

@debuffjp/dev

  • 5642db8 New: deterministic replays. With devTools({ input }), the dev tools record every key, pointer and param change from page load. The overlay's log tab has "save replay" and "copy replay", window.__DEBUFF_DEV__.replay(toTick?) returns one, and the MCP server's game_replay tool saves one to .debuff/replays/. A replay is a scenario, so debuff scenario <file> plays the session back exactly. Pass a note's tick to reproduce what the human saw when they flagged it. Anything a replay can't reproduce, such as slow motion, is listed in warnings.
  • 0bf6f5b Scenarios can run game commands: register them with devTools({ input, commands: { warp: (to) => ... } }) and call them with { "command": "warp", "args": ["hollow"] }, so a scenario can start at a checkpoint without test-only code in the game. Commands are recorded in replays. debuff scenario --update now rewrites only baselines that changed. debuff fuzz samples state during long holds and reports loop findings when a state field keeps flipping between two values with input unchanged, such as stairs that send the player straight back.
  • 95cfe26 New @debuffjp/ui package: Transition (fade out, swap, fade in), Menu (keyboard, gamepad and touch navigation through input actions) and panel (framed boxes). No dependencies. The dev overlay now attributes errors in it to debuff.

@debuffjp/cli

  • f300156 New: debuff fuzz plays your game headlessly with random but plausible input: movement keys held, action keys tapped. It reports runtime errors, explained and source-mapped, and stuck states where the scene's inspect() stops changing. Each finding is saved as a scenario in .debuff/fuzz/ that replays it exactly, because debuff's fixed steps and seeded RNG make runs deterministic. Run debuff scenario <file> to reproduce a bug, and again after the fix to verify it. The same --seed repeats a run, and the command exits non-zero on errors, so it can gate CI. The MCP server exposes it as the fuzz tool, and new projects' AGENTS.md tells agents to use it.
  • cbb7ecc Bin paths are now written as bin/x.js instead of ./bin/x.js. npm 11 treats the ./ form as invalid and strips the command, which would have left debuff, debuff-mcp and npm create debuff with nothing to run.
  • 9f96ee5 First-run fixes: official templates download from the git tag of the create-debuff release instead of main; unknown CLI commands print "unknown command: X" and exit 1; a capture mismatch prints the actual, baseline and diff image paths (a diff image is now written) and how to accept the change; MCP root descriptions say absolute paths work.
  • 7c90762 New official template: mossfell, a top-down action-adventure (npm create debuff@latest my-game -- --template mossfell). debuff build now uses relative asset URLs, so a build works from any folder, such as itch.io, GitHub Pages or a /play/ subpath.

create-debuff

  • 23edf1a Built-in guidance for coding agents: - MCP prompts: playtest, fix-errors, tune-feel, add-mechanic and explain-stutter are step-by-step workflows built from the server's tools. They show up as slash commands in clients like Claude Code, and each keeps creative calls with the human. - New projects include an AGENTS.md (commands, layout, work loop and rules) and a CLAUDE.md that points to it, so agents start with the right context.
  • 9fb8348 Platformer template: pause menu (Esc, P or Start) built on @debuffjp/ui, with a fade on restart.

keiko 0.2.0 ​

2026-10-17

  • 28f9d49 keiko now runs games from every major web engine, verified against Phaser 4, PixiJS 8, KAPLAY, Excalibur and three.js: - Timers on the virtual clock: setTimeout, setInterval and requestIdleCallback wait on game time, so timer-driven gameplay steps deterministically. Nested zero-delay timers clamp like a browser's. Use the new sleep(ms) export to wait in real time inside a test. - WebGL: an inert WebGL 1/2 context, so WebGL-only engines boot (play({ webgl: false }) to fall back to Canvas 2D). - Pointer input: the canvas reports a real size and events carry pageX/offsetX/x, so clicks land where they were aimed. Keys go to the focused element or the game canvas. - Loading: play() waits for the game's first requests to finish; await game.settle() waits for lazy loads. Games on the virtual clock get their first frame before the test starts. Images are real <img> elements; fonts and audio decoding work. - beforeEach / afterEach, folders as CLI arguments (keiko tests/levels), and errors raised between tests are reported instead of crashing the run.

keiko 0.1.1 ​

2026-10-17

  • d359aa1 npx keiko exits cleanly on Windows (no libuv assertion after the run), and the package links to its own README as its homepage.

debuff 0.1.1 ​

2026-10-16

@debuffjp/audio

  • Republished: the 0.1.0 release was held in npm staging and can't be reused. No code changes from 0.1.0.

debuff 0.1.0 ​

2026-10-16

@debuffjp/assets

  • 1487920 Automatic texture atlases. The asset pipeline packs PNGs per preload group into atlases (MaxRects, lossless, padded and edge-extruded), in dev and builds, cached by content hash. It's on by default; configure with assets.atlas in debuff.config.ts (maxSize, padding, exclude, or false). - @debuffjp/assets: new frame(id) and frames(id) work for atlased and standalone images, and assets sharing an atlas share one request. image(id) explains what to use for atlased assets. - @debuffjp/tilemap: tilesets accept an atlas region (assets.frame(id)).
  • 0cb047e Streamed music: audio.stream(url, { bus, volume, loop }) plays long tracks through an <audio> element into a bus, with no full decode into memory, so playback starts immediately. It waits for the autoplay unlock if needed and returns pause, resume, volume(value, fade) and stop(fade). assets.url(id) returns an asset's URL without downloading it.
  • aa0314b Robustness fixes: - WebGL context loss: the renderer pauses while the context is lost and rebuilds itself on restore, re-uploading textures from their sources. No more black screen after mobile tab switches or GPU resets. game.dispose() now releases GPU resources. - Image loading no longer waits on decode(), which can stall indefinitely in background tabs and throttled iframes and left games stuck loading.

@debuffjp/core

  • 38d968c Dev tools can now pause the game and explain slow frames. The overlay gains pause, step, slow-motion and "pause on spike" controls. Its frame graph breaks each frame into update and render time, and you can click a red bar to inspect that frame (steps, quads, draw calls, long tasks) with a plain-words diagnosis. You can flag moments as notes for agents. An activity log, with undo, shows who changed what: you, an agent or a scenario. Dev hooks v2 adds pause, resume, speed, frames and notes. Core counts draw.quads and draw.drawCalls per frame.
  • 77b3998 draw.clip(x, y, w, h, fn) restricts drawing to a rectangle for UI panels, scrolling lists and minimaps. It's a GPU scissor on WebGL2 and a native clip on Canvas 2D, nested clips intersect, and the clip is restored even if fn throws.
  • f807f89 Errors are now explained, and a crash no longer freezes the game. - Core: an exception in update or render used to kill the requestAnimationFrame loop while the game still reported itself as running, so start() couldn't recover it. Now the loop stops cleanly and records game.lastError (phase, scene and tick). It emits the new hooks.error and rethrows the error so the console still shows it. start() resumes. - Dev tools: each error is labelled with whose problem it is: your code, a call debuff rejected, a debuff bug (with a prefilled report link) or something outside the game. It shows the source-mapped line, which opens in your editor, plus a plain-words hint. Repeats are counted. errors() and MCP game_errors return the same structure.
  • 2d2a302 Games now open with a short "made with debuff" card in production. It lasts 1.6 seconds, any input skips it, and it respects reduced motion. It's drawn with draw, so it needs no assets. It never shows in dev or in stepped runs (scenarios and agents). Pass splash: false to remove it, or splash: { duration } to change its length.
  • 55b9ead Reduced motion: game.reducedMotion follows prefers-reduced-motion live (or force it with the reducedMotion option), and camera shake drops to a quarter strength when it's on. Read the flag to tone down your own effects.
  • 27118f8 Runtime plugin contract (ADR 0004, phase 1). - New: game.use(plugin), game.plugins, game.hooks (beforeUpdate, afterUpdate, beforeRender, afterRender, sceneChange) and game.inspect.register(name, provider). - New: devTools(options) plugin in @debuffjp/dev. exposeDevHooks is now shorthand for game.use(devTools(options)). state() adds pluginNames and plugins. - Breaking: game.afterUpdate(fn) is replaced by game.hooks.afterUpdate(fn). Input's host now needs hooks.afterUpdate; passing a Game works unchanged.
  • f50defb Batched WebGL2 renderer behind a new draw API (ADR 0007). - New: game.draw with sprite, rect, circle, line, text, a transform stack, withCamera, blend, alpha and raw. It uses WebGL2 instanced, multi-texture batching with an automatic Canvas 2D fallback (renderer: "auto" | "webgl2" | "2d"), and sustains about 200k sprites at 60 fps on the renderer benchmark. - Breaking: Scene.render(ctx) becomes Scene.render(draw), and render hooks receive draw. game.ctx is removed. camera.apply(ctx) is replaced by draw.withCamera(camera, fn) (camera.view() returns the matrix). Sprite.draw, Particles.draw and Trail.draw take draw. - New: game.redraw(), renderer in the dev state, and @debuffjp/cli/client types.
  • 9d71ad0 Agents can now work on the game you have open. debuff dev relays agent commands to the page over Server-Sent Events, and game_start in @debuffjp/mcp attaches to that live tab when one is open (otherwise it falls back to a headless copy). The overlay shows an agent badge and logs the agent's actions, which you can undo. New MCP tools: game_pause, game_frames (per-frame breakdown) and game_notes (moments you flagged in the overlay). The WebGL2 draw-call count now includes the pending batch.
  • 71b6dd3 The brand lime is now #cffb2d, the Debuff Foundation colour. Its tints are rebuilt around it, and the change covers the splash, the dev panel, the CLI colours, the templates and the art.
  • a4f2549 Fixed pointer input throwing on every mouse move. Input passed game.toLogical without its this, so the method failed. toLogical is now bound, and Input calls it through the host. Repeats of a source-mapped error now count as one entry instead of filling the error list. We found both through the MCP server's game_errors, and the dev tools labelled the first one as a debuff bug.
  • 567c0b7 Clicking or tapping the start-up splash opens debuff.jp in a new tab. Keys still just skip it.

@debuffjp/dev

  • 518b6d5 You can collapse the dev overlay to a single header bar with fps and the agent badge, using the ▾ button or a double-click on the header. The overlay remembers the state across reloads, and the frame graph stops drawing while collapsed.
  • 29f6743 Redesigned the dev overlay. It now has a toolbar (pause, step, speed, snap, restart) and four tabs: perf, tune, state and log. - perf: frames are coloured by health, and a summary shows headroom and whether dropped frames came from the game or from outside it. Normal display jitter is no longer reported as a problem. - tune: changed values are marked, you can double-click a name to reset it, and "copy changes" copies only what you tuned. - state: shows the scene's inspect() data live. - log: errors get a header badge. Arrow keys on a focused slider no longer drive the game. New exports: summarize and HITCH. The dev hooks' errors() now come from the shared session.
  • 85ad1d7 The overlay's agent badge now stays on for as long as an agent is attached, not just for 30 seconds after its last action. It turns off when the agent stops or its process exits, and the activity log records "agent disconnected".
  • c4fe419 Branded tooling: buff appears as terminal pixel art in CLI headers and reacts to results (happy or oops). A lime palette with sky, pink, amber and violet accents colours CLI output. The dev overlay gets a live buff that blinks and looks worried on frame spikes, a colour-coded FPS chip and accent-coloured param groups.

@debuffjp/input

  • 88d2571 Embed-friendly input: keys bound to actions no longer scroll the page or trigger browser shortcuts (for example, arrows and Space in an itch.io iframe), except while the player is typing in a form field. Opt out with captureKeys: false. The canvas also suppresses the right-click menu.

@debuffjp/tilemap

  • 9a37bb7 New oneWayTolerance option for move(). A falling body whose feet are up to that many pixels inside a one-way platform still lands, snapping up onto it. Set it to the plank art's thickness, so a jump that visually reaches the platform catches it instead of dropping back through.
  • 451831a New @debuffjp/tilemap: a Tilemap with culled, allocation-free rendering through draw (with parallax and Tiled flip and rotation flags), tile collision via map.move(body, dx, dy) with one-way platforms and no tunnelling, and importers for Tiled (fromTiled) and LDtk (fromLdtk). The asset pipeline now loads .tmj, .tsj and .ldtk files as JSON.

@debuffjp/cli

  • 155638d New @debuffjp/mcp: an MCP server with project tools (inspect_project, list_assets, build, run_scenarios) and live-game tools (game_start, game_step, game_input, game_state, game_params, game_set_param, game_capture, game_errors, game_stop). Templates include .mcp.json. @debuffjp/cli exports inspectProject, launchBrowser, callHook and hooksReady.
  • ec06222 Tooling plugins (ADR 0004, phase 3): commands become debuff <plugin>:<command>, and scenarioSteps add { "<plugin>:<step>": value } steps with JSON Schemas merged into .debuff/scenario.schema.json. Also fixes debuff --help printing twice.
  • 71c6b6e The built index.html preloads the asset manifest and the default group's images or atlases, so they download alongside the game code instead of after it.
  • b25d482 Tooling plugins (ADR 0004, phase 2a). - New: debuff.config.ts with defineConfig({ plugins }), ToolingPlugin with function and command (any language, JSON over stdio) asset importers, cached by content hash. - Breaking: source assets move from public/assets/ to assets/. The manifest is generated in memory and is no longer written to disk, and builds ship only files the manifest references. - debuff assets --json and debuff inspect report importers and plugin contributions.

create-debuff

  • f609f0b New official platformer template (npm create debuff -- --template platformer): buff runs and jumps through a Tiled level with tile collision, one-way platforms, coins, a goal, an automatically built atlas, coyote time, jump buffering and fully tunable movement. Official templates other than basic download on demand from the debuff repo, so they never grow the create-debuff package. create() and resolveTemplate() are new exports.
  • ab4a23d Community templates from git (ADR 0004, phase 4): --template github:user/repo[/subdir][#ref], plus gitlab:, bitbucket: and sourcehut:. Downloads use giget, so no git binary is needed.