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.
| Project | Latest |
|---|---|
| debuff | 0.27.0 |
| spryte | 0.12.0 |
| keiko | 0.7.0 |
| myaku | 0.5.1 |
| plonk | 0.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
positionoption 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, whetherDEBUFF_BROWSERwas 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, wherenavigator.clipboarddoesn'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 thedev_hooks_missingerror 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 matchesdraw.spritepixel 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
mipmapsoption forcreateRenderer: 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 andsmooth: falsedraws 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)andsmooth.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 theprevX/prevYbookkeeping and lerp that games wrote themselves. While the game is frozen (game.freeze()),renderand the render hooks now getalpha1 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 byalpha, jumped back and forth on every frame of a hitstop. - 0228a0a
SpatialHashanswers "what's near this" without checking every pair:rebuild(items)once per step, thennear(x, y, radius, visit)for every item within the radius (returntrueto stop) ornearest(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 haveclips(on)andsaveClip(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: trueongame_capture, and adevlog-postprompt that captures a still and a clip and drafts short posts for the human to send. Agents never post. -debuff devwrites captures through a new/__debuff/captureendpoint, behind the same own-origin guard as the others. -debuff ship pressadds 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
TouchControlsputs a stick and buttons on screen for phones and tablets, feeding the same actions as keys and pads: bindTouchStickLeft,TouchStickRight,TouchStickUp,TouchStickDownandTouch<name>(TouchA) next toArrowLeftandPadA. The stick is fixed or floating (centred where the thumb lands in a zone), analog throughtouch.stick.xandy, 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 withpointer.touchStart/touchMove/touchEnd.Inputhas avirtualmap for sources that code sets each step, whichTouchControlsuses.
@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 scenariosays 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-savesis safe then. - Thelaunch.urlwarning says where to set it (launch: { url }in debuff.config.ts). -debuff testoutside a project says there's no package.json, instead of "No tests yet". -debuff fuzzprints a line after each run, so its default three minutes aren't silent. -npm create debuff's next steps describenpm testas 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.uvstays in atlas space, so effects shaped to a sprite no longer need the frame's atlas rect passed as a uniform.@debuffjp/corenow re-exports theShaderandUniformValuetypes.
@debuffjp/dev
- e44fbbc
state()hashidden: true while the tab is in the background, where the browser stops animation frames andtickstands 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_CLOSINGassertion and exit code 127, sodebuff build && tscstopped 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 devreports 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 onExplainedError.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 withdebuff papercuts off, as before. - f343dd3 Runtime errors thrown inside spryte, plonk or myaku are now explained as debuff's (
debuff-usageordebuff-bug, with a report link) instead ofyour-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.mdand the agent skills say so. Sound files you provide still play throughaudio.play.
spryte 0.11.0
2026-10-31
- b17a196 Static layers:
draw.record(fn)keeps the spritesfndraws in aLayerwithout drawing them, anddraw.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.alphaapplies at replay. On Canvas 2D, under a custom shader ordraw.alphabelow 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.uvstays in atlas space, so effects shaped to a sprite no longer need the frame's atlas rect passed as a uniform.@debuffjp/corenow re-exports theShaderandUniformValuetypes.
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 papercutandreport_papercutshow 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 "…"andreport_papercutwithhitsend the workaround, which fills the papercut's workaround when it has none. Before, it was dropped.--hitsays which other flags it ignores, and links point atpapercuts.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 upgrademoves 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 withDEBUFF_UPDATE_CHECK=0and in CI.debuff devnow prints the daily update hint under its banner. It never exits, so it used to never show the hint.debuff doctorreports hooks from 0.23 as out of date.--fixnow rewrites a.claude/settings.jsonor.codex/hooks.jsonthat 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 papercutand the MCP toolreport_papercutreport 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 withdebuff papercuts off,DEBUFF_PAPERCUTS=0orDEBUFF_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.jsonfor Claude Code and.codex/hooks.jsonfor Codex) that checks the agent's edits and commands as it makes them: - It asks the human before screenshot baselines (including the MCPrun_scenarioswithupdate) or saves change. - It refuses edits to.debuff/,dist/andnode_modules/, a foregroundnpm run dev, and hand-changed@debuffjp/*versions. - It warns about newMath.random,Date.nowand real-timer lines insrc/, which break replays and scenarios. - Once per stop, it asks fordebuff testwhensrc/changed since the last run.debuff test,debuff scenarioand the MCPrun_testsandrun_scenariosnow record their runs in.debuff/last-testfor this. The rules come from the installed CLI (@debuffjp/cli/hook). The hook does nothing beforenpm install, andDEBUFF_HOOK=0turns it off.debuff doctorhas a new advisoryagent-hookscheck, and--fixwrites the hook files that are missing without touching existing settings. See debuff.jp/docs/agents#hook. - 6fe654d New
isolationoption indebuff.config.tsfor games that useSharedArrayBufferand worker threads.debuff devsends the cross-origin isolation headers,debuff buildwrites them todist/_headersfor Cloudflare and Netlify (added to a_headersyou ship yourself),debuff doctorwarns about filesindex.htmlloads from other sites, anddebuff ship itchreminds you to tick itch.io's SharedArrayBuffer option.isolation: trueusesembed: "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 itsbegin()andend(), to tell a GPU-bound frame from a CPU-bound one. Results arrive a few frames late and never block.msstaysundefinedwhere timing isn't available (Canvas 2D, and browsers withoutEXT_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. Seteach.strideandpositionsbecomes one record ofstridefloats per copy, uploaded to the GPU as is;each.scale,rotation,colorandframethen take offsets into the record.positionsmust be aFloat32Arrayand may view aSharedArrayBuffer, so a worker's buffer draws without a per-frame copy.sprites()insidewithShadernow stays one instanced draw instead of one quad per copy: the effect is compiled against the per-copy program. Onlysource(uv),coloranduvare guaranteed insideeffectthere. 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 usedraw.spriteper 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 buildandnpx debuff assetswrite.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 intsconfig.jsonand rundebuff buildbeforetsc. In an existing project, add".debuff/assets.d.ts"toincludeand rundebuff buildbeforetsc; without the file, ids stay plain strings as before.AssetId<T>andAssetGroupare exported for your own signatures.
create-debuff
- af2583c The
debuffagent 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
debuffagent skill now also loads when asked to make the player jump higher or run faster, make the game look nicer, or whennpm 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
debuffagent 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. Runnpx debuff doctor --fixto update a project's skills.
debuff 0.21.0
2026-10-28
@debuffjp/cli
- 6b68789
debuff upgrademoves 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'sdoctor --fix, which rewrites the agent skills for it.--dry-runshows the plan; it never downgrades without--to.debuff doctornow 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 withDEBUFF_UPDATE_CHECK=0). - dbb1861
debuff doctorgives the fix (npm install) whendebuff.config.tsfails only because nothing is installed yet, anddoctor --fixandupgradeno 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 ondebuff 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.jsonwith tasks for the dev server, tests, build and doctor. - 6b1c064 A command with nothing to act on now says so with its own code:
scenarioin a project with no scenarios andreportwith no build exit 2 withnothing_to_runand a fix, instead ofunexpected.
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,
debuffFrameand 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;doctoritself 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 stablecode. Unknown options are now an error instead of being ignored, andscenario --jsonno 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 debuffwrites thedebuff,debuff-setup,keikoandmyakuskills into.claude/skills/(Claude Code) and.agents/skills/(Codex, Gemini CLI, Cursor, Copilot), each stamped withmetadata.debuff-version.debuff doctorflags missing or stale skills, and--fixrewrites 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,
debuffFrameand 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/heightare 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: spreadsquishintodrawPanelorpanelAround, and passsquishBartobar, to keep the previous look.neutralLightsuits light games. The focus pill's squash is now thesquashoption, on insquishand off by default.debuffFrameis deprecated in favour ofsquish.
debuff 0.18.0
2026-10-27
@debuffjp/cli
- c7efeba
debuff doctor --fixapplies 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 todebuff doctor. The MCP server's instructions get a missing space back.
debuff 0.17.0
2026-10-27
@debuffjp/dev
- 77f659e The
luminancedev hook, which theflashesandcontrastscenario checks read, now averages every pixel in each cell itself. It used to shrink the frame withdrawImage, 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 doctorchecks a project's setup in one pass and prints a fix for each problem: Node version, debuff packages on one version,node_modulesmatchingpackage.json, a nested copy of@debuffjp/core,@debuffjp/devand TypeScript,debuff.config.ts, the asset pipeline,saves.json, agent files (AGENTS.md,CLAUDE.md,.mcp.json), the browser scenarios use, and aNODE_ENVleft set in the shell.--livealso starts the dev server and checks the game exposes its dev hooks. It exits 1 when a required check fails, and--jsonprints every check with an id. The MCP server gains adoctortool, and new projects'AGENTS.mdlists the command.
debuff 0.16.0
2026-10-27
@debuffjp/dev
- efec916 The dev hooks'
pointerDown(x, y, kind?)andtap(x, y, kind?)take what is pressing:"mouse"(the default, as before),"touch"or"pen". The game sees it inpointer.kind. A touch or pen press is contact 1 inpointer.touchesand 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'sgame.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 throughinput.down/pressed/released, scenarios and fuzz like any key.input.pointer.rightandinput.pointer.middleholddown,pressedandreleased, including buttons pressed while another is held;pointer.pressButton("right")andreleaseButtonsimulate them. The dev hooks'press("PointerRight"), scenario{ "press": "PointerRight" }anddebuff fuzz --keys PointerRightpress 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?), andgame.touchDown(id, x, y),touchMove(id, x, y)andtouchUp(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 apointermove, as in browsers, and the right button firescontextmenu),wheelevents, andpointerType: "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)andtouchUp(id). Recorded replays keep wheel scrolls and touches, so a session played on a phone replays exactly. They need@debuffjp/input0.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.kindsays what made the latest press:"mouse","touch"or"pen", so games don't need their ownpointerdownlistener to readpointerType. Contacts inpointer.touchescarry akindtoo, andpointer.touchesStarted/pointer.touchesEndedlist the contacts that started and ended this step, so games don't diff the map themselves.pointer.touchStart(id, x, y, kind?),touchMoveandtouchEndsimulate 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 ofkeysand updatesinput.keys.captured, so newly bound keys stop scrolling the page and keys no longer bound scroll it again; keys the game added tocaptureditself are left alone.input.bindingsis 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.wheelandinput.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
preventDefaultonpointerdownstopped the click focusing the frame, so keys went nowhere until the player clicked outside the game. A press now callswindow.focus()when the document doesn't have focus. - fbd1160 A press is no longer dropped when
setPointerCapturethrows. Synthetic pointer events and unknown pointer ids make it throwNotFoundError, which stopped the press from registering. The pointer now tries to capture and carries on if it can't.
@debuffjp/cli
- e17b3df
debuff fuzz --keysaccepts the gesturesWheel(a random scroll) andTouch(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.videoindebuff.config.ts: point it at a short looping MP4 anddebuff buildcopies it todist/preview.mp4and addsog:videotags, size included, so Discord and other apps that play link previews show the clip. - b87c265 Scenario fixes in
debuff scenarioand keiko'sgame.replay(): -{ "tap": "KeyT" }taps the key for one step, as keiko'sgame.tap("KeyT")does. Before,debuff scenariosilently spread the string intotap(x, y), and keiko pressed the wrong key. The schema accepts a key code. Atapthat is neither a code nor a point fails with an explanation. -{ "expect": { "state.x": 0 } }(and the same inuntil) fails withstate.x: expected a comparison object like { "eq": 0 }, got 0, instead of a TypeError or a silent pass. An object with none ofeq,gt,gte,lt,lteandapproxis reported the same way.
@debuffjp/mcp
- fead3cd
game_inputgainswheel(dy,dx),touch_down/touch_move/touch_up(by contactid, withkind: "pen"for a pen), and akindontapandpointer_down.
spryte 0.7.0
2026-10-27
- a5d8af2
spryte/pixi: draw with spryte inside a PixiJS v8 app through aRenderContainer, 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?)andtap(x, y, kind?)take what is pressing:"mouse"(the default, as before),"touch"or"pen". The game sees it inpointer.kind. A touch or pen press is contact 1 inpointer.touchesand 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'sgame.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?), andgame.touchDown(id, x, y),touchMove(id, x, y)andtouchUp(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 apointermove, as in browsers, and the right button firescontextmenu),wheelevents, andpointerType: "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)andtouchUp(id). Recorded replays keep wheel scrolls and touches, so a session played on a phone replays exactly. They need@debuffjp/input0.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 sendspointerIdid + 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 scenarioand keiko'sgame.replay(): -{ "tap": "KeyT" }taps the key for one step, as keiko'sgame.tap("KeyT")does. Before,debuff scenariosilently spread the string intotap(x, y), and keiko pressed the wrong key. The schema accepts a key code. Atapthat is neither a code nor a point fails with an explanation. -{ "expect": { "state.x": 0 } }(and the same inuntil) fails withstate.x: expected a comparison object like { "eq": 0 }, got 0, instead of a TypeError or a silent pass. An object with none ofeq,gt,gte,lt,lteandapproxis reported the same way.
spryte 0.6.0
2026-10-27
- 209fc38
spryteExterndraws images Phaser already loaded from Phaser's own GPU textures, so a texture shared by both is no longer uploaded twice.createSharedRenderertakes aborrowoption 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, anddevOverlay({ open: true })keeps it visible.state().pluginsleaves out debuff's own tools (providers nameddev-*, 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:
topdownandisometric(a tileset, buff with walk animations, pickups),sidescroller(run, jump, one-way planks) andpointclick(rooms, hotspots, an inventory, speech). Squish is listed as "Match 3". Templates declarekindintemplate.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 atawait game.run()instead ofwait().
debuff 0.14.0
2026-10-23
@debuffjp/assets
- 7bf806f New:
reload(id)re-fetches an asset in place andonChange(id, listener)hears about it;debuff devuses 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 everyonChangelistener 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) => ...)wrapsbeforeRenderand the scene's render, so a plugin can draw the world through a render target,filter()or shader.afterRenderstays outside it. -game.emitcalled from agameEventlistener now waits until the current event has reached every listener. Before, later listeners saw the second event first. -@debuffjp/core/testingexportscreateTestGame(), aGamethat runs under happy-dom or jsdom for unit-testing plugins.
@debuffjp/dev
- ca6ba6a
state()addscamera(centre, zoom and view size in world units) so external harnesses can placeinspect()rows in the view.
@debuffjp/cli
- 7bf806f
debuff devre-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
.ttsprites drawn with tenten, the pixel-art MCP server for agents, become PNGs and sprite sheets inassets/with no configuration, like.asepritefiles do withaseprite(). tenten is found in$TENTENor the project, and otherwise fetched withnpx -y tententhe first time a.ttfile appears;tenten({ command })overrides that. New projects'.mcp.jsonlists 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.spritestakes a frame per copy: pass aFrame[]as the first argument and index it witheach.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 andwithShadertake the same path as before.
spryte 0.4.3
2026-10-23
- 927bbd0 Ten fixes from a review of the whole package: -
roundRectlost 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()andfilter()insidedraw.clip()on Canvas 2D leaked the clip'ssave()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()andfilter()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 leavesinto()'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
createSharedRendererstill 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 polledisContextLost()atbegin(), 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 nextbegin().
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/phaserwraps it for Phaser 4:spryteExtern(extern, render)draws in anExtern's slot with Phaser's camera applied, andcreatePhaserContext()gives Phaser a WebGL2 context with its shaders translated to GLSL ES 3.00 (Phaser 4.2.1'sGradient, noise normal maps andsmoothPixelArtdon't compile on WebGL2 otherwise). Alsodraw.transform(a, b, c, d, e, f)multiplies a matrix onto the transform, anddraw.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, followingtempo(); -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/audiogainsaudio.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_commandtool runs a game's dev command by name with JSON args.game_starttakesviewport: [w, h](headless) andreducedMotionto force it on or off.game_framestakessummary(percentiles and the 3 slowest frames),game_steptakesquiet(no inspect state), andgame_capturetakespathto save the PNG in the project.game_profilein headless mode now says profiling needs live mode instead of blaming requestAnimationFrame. - dev:state()reports the logicalwidth/height, andreducedMotionandreducedMotionForced; newreducedMotion(value | null)hook forces it. - core:Random#stategets and sets the generator's state, for undo and replays. - keiko:toBeLessThan/toBeGreaterThanmatchers; docs say-tis a case-insensitive substring match and point async game logic atrun()/settle()instead ofwait(). - MCP: headless errors are now just the message (nopage.evaluate:prefix or stack). - MCP, scenarios and dev: calling a hook an older@debuffjp/devdoesn't have now says to update it, instead of "is not a function". - 7a1135e New
scale: "expand"mode for responsive games. Likefit, but instead of letterboxing, the logical size grows along the spare axis to fill the container:width/heightbecome the minimum,game.width/game.heightfollow the container, andgame.hooks.resize(fn)fires when they change. One game can be portrait on phones and landscape on a desktop portal. - c18bf4b In dev builds,
Gamenow warns when it starts with no active scene ("callgame.go(id)first"), or when scenes were added butgame.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).
Storageuses 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 buildfinds every save, compares it withsaves.json(committed, like a lockfile), fails if a save key disappears and warns when a field's type changes without a version bump;--accept-savesrecords intended changes.debuff inspectand the MCPbuildtool include saves, and the templates ship asaves.json.
debuff 0.10.0
2026-10-21
@debuffjp/dev
- ea6a86a Type errors while you play: set
typecheck: trueindebuff.config.tsanddebuff devtype-checks with your project's owntscafter every save. Type errors show as a badge and cards in the dev overlay (the game keeps running), inwindow.__DEBUFF_DEV__.typeErrors(), and to agents through the newtypecheckMCP tool, which also works without a browser.
create-debuff
- 698f6f0 The basic template turns on
typecheckin itsdebuff.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_commandtool runs a game's dev command by name with JSON args.game_starttakesviewport: [w, h](headless) andreducedMotionto force it on or off.game_framestakessummary(percentiles and the 3 slowest frames),game_steptakesquiet(no inspect state), andgame_capturetakespathto save the PNG in the project.game_profilein headless mode now says profiling needs live mode instead of blaming requestAnimationFrame. - dev:state()reports the logicalwidth/height, andreducedMotionandreducedMotionForced; newreducedMotion(value | null)hook forces it. - core:Random#stategets and sets the generator's state, for undo and replays. - keiko:toBeLessThan/toBeGreaterThanmatchers; docs say-tis a case-insensitive substring match and point async game logic atrun()/settle()instead ofwait(). - MCP: headless errors are now just the message (nopage.evaluate:prefix or stack). - MCP, scenarios and dev: calling a hook an older@debuffjp/devdoesn'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,screenanderase, alongsidenormalandadd. WebGL2 and Canvas 2D draw them identically over opaque pixels (checked on a real GPU). - a5e021b Filters:
filter(draw, filters, fn)drawsfnthrough a chain of ready-made effects:blur(),grayscale(),sepia(),saturate(),hueRotate(),brightness(),contrast(),invert()andcolorMatrix(). 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 whatfndraws only whereshapedraws. 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 toclip()on both backends. - d7ec5ea
nineSlice(draw, …)for UI panels that stretch without stretching their corners, andtiled(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,
nineSliceandtiledare standalone functions that take theDraw(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) andring. 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 CSSblur(), 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.circlestays 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.textwith 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
panper instrument. Musical vocabulary: songkeyandtranspose, 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. Andrender(),analyse()andmix()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/audiogainsaudio.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
padstep.
@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, andgame.dispose()frees every GPU object. New:draw.fontsets the default font fordraw.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 (
debuffFrameis this look;FrameStyle.radiusis 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),screenshotanddownload.
@debuffjp/cli
- 61798e2
debuff buildnow adds link-preview tags (Open Graph and Twitter), a web app manifest and a preview image (your first scenario baseline) todist/, configured bylaunchindebuff.config.ts. Newdebuff ship itch|wavedash|pressgets a build ready for a store: an itch.io zip with limit checks, Wavedash config andWavedash.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, andtweens.has(target, prop?)says whether something is still animating. - Scenarios and fuzz: set the browser viewport withscenarios: { viewport: [360, 640] }indebuff.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 buildregisters 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 withdebuff telemetry offorDEBUFF_TELEMETRY=0; it is off in CI. Details: https://debuff.jp/docs/telemetry - 77fe8c6 Fix: a project with a
debuff.config.tsbuilt in development mode (loading the config through Vite setNODE_ENV=development), sodebuff buildfailed its dev-leak check or, worse, could ship the dev tools. Loading the config now leavesNODE_ENVas 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, andDrawsnaps each sprite once, after the camera. - Scenariocontrast: 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/--filterno 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 callsplay()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
1e3mswork, andpageaccepts 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 initwrites a first test for the engine your project uses and prints a starterwindow.__KEIKO__hook.game.enginereads 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.deterministicdivergence between runs with different seeds suggests{ sameSeed: true }when the game takes its randomness fromMath.random(), and anuntil()oreventually()that times out with the state unchanged since the last pointertap()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()andDate()now run on the virtual clock, not justDate.now().test.deterministicnow 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-coreis now an optional peer dependency, so using only the in-browser profiler no longer installs it.myaku recordusers neednpm 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.--traceignores 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 --traceno 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 --tracerecords 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, andunknownhitches namedgpuorcompositorfrom evidence. A lone GPU stall on a lightly loaded GPU staysunknownand says it's likely outside the game.--trace-outsaves 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.budgetMscan 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
updateandrenderspans for Excalibur (found automatically) and KAPLAY (automatic with its default globals, oradapt(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()andDate()now run on the virtual clock, not justDate.now().test.deterministicnow 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.spritesclampscountto the arrays it's given (it used to draw nothing on WebGL2 whencountwas too large); Canvas 2D keeps smoothing correct after a clip; oversized or fractional render targets throw clearly; using a disposedDrawthrows. 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), andbar()draws a segmented meter that lights whole, equal cells.debuffFrameis 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.textand menus now default to debuff sans, a built-in pixel font (printable ASCII, 5x7 capitals, proportional), instead of the browser's8px monospace.debuffSans()returns it as aBitmapFont. Passfont: "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_rewindtool, for example at a note's tick, and games withwindow.__DEBUFF_DEV__.rewind(tick). save as scenario in the panel writes the session so far toscenarios/<name>.jsonthroughdebuff 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 instate().events, and__DEBUFF_DEV__.events()lists the latest with their tick and detail. - Scenarios have anuntilstep:{ "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 await.expectanduntilreadevents.<name>as 0 until the first emit. - keiko replaysuntilsteps and addsgame.emitted("coin"). - New projects'AGENTS.mdtells agents to prefer state and event assertions over screenshots, and the platformer emitscoinandwin, with acollect-coinscenario 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 buildreports code at startup separately from code loaded on demand. - b37f2d9 Document turning off the splash for portals: the
splashoption's docs and new projects' AGENTS.md say Poki requiressplash: 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": trueruns a scenario withprefers-reduced-motion: reduce. These are screening checks on what the game actually draws, not a certification. The dev hooks gainluminance(cols, rows, region?). keiko keeps time moving throughflashessteps and skipscontrast, since nothing is drawn headless. The platformer ships anaccessibilityscenario, and new projects'AGENTS.mdasks 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 withdevTools({ 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, includingNO_COLOR/FORCE_COLORand the flag syntax plugin commands use.
@debuffjp/mcp
- 27dce10
@debuffjp/mcpdeclaresmcpName: "jp.debuff/debuff"for the official MCP Registry.
keiko 0.4.1
2026-10-18
- 444da77
npx keikoruns 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 ofUnexpected 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/devstarts it with debuff's update and render as spans and addsprofile(); the MCP server addsgame_profile, and the explain-stutter prompt uses it.
create-debuff
- 793eab1 The "Next steps" note mentions
npm testwhen 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 instate().events, and__DEBUFF_DEV__.events()lists the latest with their tick and detail. - Scenarios have anuntilstep:{ "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 await.expectanduntilreadevents.<name>as 0 until the first emit. - keiko replaysuntilsteps and addsgame.emitted("coin"). - New projects'AGENTS.mdtells agents to prefer state and event assertions over screenshots, and the platformer emitscoinandwin, with acollect-coinscenario 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": trueruns a scenario withprefers-reduced-motion: reduce. These are screening checks on what the game actually draws, not a certification. The dev hooks gainluminance(cols, rows, region?). keiko keeps time moving throughflashessteps and skipscontrast, since nothing is drawn headless. The platformer ships anaccessibilityscenario, and new projects'AGENTS.mdasks 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, includingNO_COLOR/FORCE_COLORand the flag syntax plugin commands use.
keiko 0.3.1
2026-10-18
- 27d109a When
test.deterministicfinds 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 toeventually().onStephooks 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,--p99or--hitchesbudgets are exceeded.--playdrives the game with a Playwright script,--cputhrottles the CPU,--json/--outwrite the report. Addsprofiler.reset().
myaku 0.1.0
2026-10-18
- c49aac3 Engine spans:
updateandrenderfor PixiJS 8 and three.js (found automatically through their devtools hooks), Phaser 3 or any globalPhaser(withupdate:<scene>per scene), andadapt(profiler, game)for Phaser 4's ES modules and anything else. Turn off withmyaku({ 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/devstarts it with debuff's update and render as spans and addsprofile(); the MCP server addsgame_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.heapnow checks that heap readings actually change, andunknownverdicts say when GC can't be ruled out. Span names past 16 are added up underotherinstead 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 asfonttodraw.text,draw.measureand@debuffjp/uimenus. The CLI shipsaseprite/new-font.lua, which creates a blank font sheet with the grid set up. - 134c84a
draw.measure(text, font)returns the boxdraw.textfills.@debuffjp/uinow 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()reportsstep, 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 withpointer) toupdate.
@debuffjp/cli
- 91f7088
debuff testruns 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;--jsongives a report for tools. - 01c7c17
aseprite()tooling plugin:.aseprite/.asefiles inassets/are exported by Aseprite automatically. Multi-frame files become sprite sheets (assets.frames(id)),name@WxH.asepriteflattens to a grid (forBitmapFont), hidden layers are skipped, and results are cached. Aseprite is found via$ASEPRITE,PATHor 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/andkeikoas a dev dependency;npm testruns logic tests, then scenarios.AGENTS.mdexplains when to use which.
@debuffjp/mcp
- 91f7088 New
run_teststool runs the project's keiko logic tests; failures carry the tick, scene and state where they broke. Theadd-mechanic,fix-errorsandtune-feelprompts now ask for a logic test. - 97c44ec Every tool now declares
readOnlyHintordestructiveHint, 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. Addsrun()andeventually()for games whose UI flows are async, and aviewportoption.
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 byGamebefore 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 thealphapassed torender. - 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.jpis 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'sgame_replaytool saves one to.debuff/replays/. A replay is a scenario, sodebuff 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 inwarnings. - 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 --updatenow rewrites only baselines that changed.debuff fuzzsamples state during long holds and reportsloopfindings when a state field keeps flipping between two values with input unchanged, such as stairs that send the player straight back. - 95cfe26 New
@debuffjp/uipackage:Transition(fade out, swap, fade in),Menu(keyboard, gamepad and touch navigation through input actions) andpanel(framed boxes). No dependencies. The dev overlay now attributes errors in it to debuff.
@debuffjp/cli
- f300156 New:
debuff fuzzplays 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'sinspect()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. Rundebuff scenario <file>to reproduce a bug, and again after the fix to verify it. The same--seedrepeats a run, and the command exits non-zero on errors, so it can gate CI. The MCP server exposes it as thefuzztool, and new projects'AGENTS.mdtells agents to use it. - cbb7ecc Bin paths are now written as
bin/x.jsinstead of./bin/x.js. npm 11 treats the./form as invalid and strips the command, which would have leftdebuff,debuff-mcpandnpm create debuffwith 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
rootdescriptions say absolute paths work. - 7c90762 New official template:
mossfell, a top-down action-adventure (npm create debuff@latest my-game -- --template mossfell).debuff buildnow 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-mechanicandexplain-stutterare 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 anAGENTS.md(commands, layout, work loop and rules) and aCLAUDE.mdthat 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,setIntervalandrequestIdleCallbackwait on game time, so timer-driven gameplay steps deterministically. Nested zero-delay timers clamp like a browser's. Use the newsleep(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 carrypageX/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 keikoexits 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.atlasindebuff.config.ts(maxSize,padding,exclude, orfalse). -@debuffjp/assets: newframe(id)andframes(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 returnspause,resume,volume(value, fade)andstop(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 ondecode(), 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,framesandnotes. Core countsdraw.quadsanddraw.drawCallsper 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 iffnthrows. - f807f89 Errors are now explained, and a crash no longer freezes the game. - Core: an exception in
updateorrenderused to kill the requestAnimationFrame loop while the game still reported itself as running, sostart()couldn't recover it. Now the loop stops cleanly and recordsgame.lastError(phase, scene and tick). It emits the newhooks.errorand 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 MCPgame_errorsreturn 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). Passsplash: falseto remove it, orsplash: { duration }to change its length. - 55b9ead Reduced motion:
game.reducedMotionfollowsprefers-reduced-motionlive (or force it with thereducedMotionoption), 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) andgame.inspect.register(name, provider). - New:devTools(options)plugin in@debuffjp/dev.exposeDevHooksis now shorthand forgame.use(devTools(options)).state()addspluginNamesandplugins. - Breaking:game.afterUpdate(fn)is replaced bygame.hooks.afterUpdate(fn).Input's host now needshooks.afterUpdate; passing aGameworks unchanged. - f50defb Batched WebGL2 renderer behind a new
drawAPI (ADR 0007). - New:game.drawwithsprite,rect,circle,line,text, a transform stack,withCamera,blend,alphaandraw. 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)becomesScene.render(draw), and render hooks receivedraw.game.ctxis removed.camera.apply(ctx)is replaced bydraw.withCamera(camera, fn)(camera.view()returns the matrix).Sprite.draw,Particles.drawandTrail.drawtakedraw. - New:game.redraw(),rendererin the dev state, and@debuffjp/cli/clienttypes. - 9d71ad0 Agents can now work on the game you have open.
debuff devrelays agent commands to the page over Server-Sent Events, andgame_startin@debuffjp/mcpattaches 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) andgame_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.
Inputpassedgame.toLogicalwithout itsthis, so the method failed.toLogicalis now bound, andInputcalls 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'sgame_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:summarizeandHITCH. 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
oneWayToleranceoption formove(). 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: aTilemapwith culled, allocation-free rendering throughdraw(with parallax and Tiled flip and rotation flags), tile collision viamap.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,.tsjand.ldtkfiles 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/cliexportsinspectProject,launchBrowser,callHookandhooksReady. - ec06222 Tooling plugins (ADR 0004, phase 3):
commandsbecomedebuff <plugin>:<command>, andscenarioStepsadd{ "<plugin>:<step>": value }steps with JSON Schemas merged into.debuff/scenario.schema.json. Also fixesdebuff --helpprinting twice. - 71c6b6e The built
index.htmlpreloads 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.tswithdefineConfig({ plugins }),ToolingPluginwith function and command (any language, JSON over stdio) asset importers, cached by content hash. - Breaking: source assets move frompublic/assets/toassets/. The manifest is generated in memory and is no longer written to disk, and builds ship only files the manifest references. -debuff assets --jsonanddebuff inspectreport 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 thanbasicdownload on demand from the debuff repo, so they never grow the create-debuff package.create()andresolveTemplate()are new exports. - ab4a23d Community templates from git (ADR 0004, phase 4):
--template github:user/repo[/subdir][#ref], plusgitlab:,bitbucket:andsourcehut:. Downloads use giget, so no git binary is needed.