{"_id":"@caputchin/preset-kaplay","_rev":"3-0b0e6c8c9ccdb5a547351a0b925434ca","name":"@caputchin/preset-kaplay","dist-tags":{"latest":"0.4.0"},"versions":{"0.2.0":{"name":"@caputchin/preset-kaplay","version":"0.2.0","keywords":["caputchin","kaplay","kaboom","deterministic","replay","captcha","game","preset"],"license":"Apache-2.0","_id":"@caputchin/preset-kaplay@0.2.0","maintainers":[{"name":"caputchiner","email":"info@caputchin.com"}],"homepage":"https://github.com/Caputchin/caputchin-sdk#readme","bugs":{"url":"https://github.com/Caputchin/caputchin-sdk/issues"},"dist":{"shasum":"d2d7d8d7fcabdddb5c13bf10f56c0d1779e9e54c","tarball":"https://registry.npmjs.org/@caputchin/preset-kaplay/-/preset-kaplay-0.2.0.tgz","fileCount":7,"integrity":"sha512-0eWMfD19O6tPL/gFWmNufbckCLlCYXnsYqjuSmljNMIrvZ58HTO+fix+Flmd/fXaPiTvsRsP/pFIGaCPKAPYWA==","signatures":[{"sig":"MEUCIQC1nTQsuqfifJbDvSmK+RQbZiops4kbKVE+TSljeM8UTQIgPFzK7GC+Gs6oPKqLFbJiKicQOTliR8o0LQET7h1Xyno=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":91222},"type":"module","_from":"file:caputchin-preset-kaplay-0.2.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"docs":"typedoc && node ../../scripts/normalize-docs.mjs docs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"caputchiner","email":"info@caputchin.com"},"_resolved":"/tmp/a5db74b89984f8089e0e8923eebd027f/caputchin-preset-kaplay-0.2.0.tgz","_integrity":"sha512-0eWMfD19O6tPL/gFWmNufbckCLlCYXnsYqjuSmljNMIrvZ58HTO+fix+Flmd/fXaPiTvsRsP/pFIGaCPKAPYWA==","repository":{"url":"git+https://github.com/Caputchin/caputchin-sdk.git","type":"git","directory":"presets/kaplay"},"_npmVersion":"11.13.0","description":"Self-contained Caputchin replay preset for the KAPLAY engine: headless boot, fixed-step pump, deterministic RNG rail, and the conforming run/live adapters. The on-ramp for any KAPLAY game on the deterministic-replay platform.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","dependencies":{"@caputchin/determinism":"0.2.0","@caputchin/replay-contract":"0.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","kaplay":"3001.0.19","vitest":"^2.0.0","typedoc":"^0.28.19","happy-dom":"^15.0.0","typescript":"^5.4.0","@types/node":"^22.0.0","@caputchin/game-sdk":"3.1.0","typedoc-plugin-markdown":"^4.11.0"},"peerDependencies":{"kaplay":"3001.0.19","@caputchin/game-sdk":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/preset-kaplay_0.2.0_1780536368354_0.786560461585949","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@caputchin/preset-kaplay","version":"0.3.0","keywords":["caputchin","kaplay","kaboom","deterministic","replay","captcha","game","preset"],"license":"Apache-2.0","_id":"@caputchin/preset-kaplay@0.3.0","maintainers":[{"name":"caputchiner","email":"info@caputchin.com"}],"homepage":"https://github.com/Caputchin/caputchin-sdk#readme","bugs":{"url":"https://github.com/Caputchin/caputchin-sdk/issues"},"dist":{"shasum":"4dee01271ec181dea7c6537a791f700bbe727641","tarball":"https://registry.npmjs.org/@caputchin/preset-kaplay/-/preset-kaplay-0.3.0.tgz","fileCount":7,"integrity":"sha512-YngVwRmcDswm8EpT+ZYMlb1yiV0Bm7c6Y6Mkx/I2Wmsl7gxGsW86AFtSUVGlwmUzA90tyZizGgiYZ9OK65iArg==","signatures":[{"sig":"MEUCIQDiVlGJunlzxRDpTMQI52vpGskhu/oSxt/HdRMY6ZQwVwIgBh6uL6u4dFnOL43Xer6v3YGDU7kkl18shqacEF7KMrE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":92850},"type":"module","_from":"file:caputchin-preset-kaplay-0.3.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"docs":"typedoc && node ../../scripts/normalize-docs.mjs docs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"caputchiner","email":"info@caputchin.com"},"_resolved":"/tmp/f13f177caab9b8e0a26c2344fa78de29/caputchin-preset-kaplay-0.3.0.tgz","_integrity":"sha512-YngVwRmcDswm8EpT+ZYMlb1yiV0Bm7c6Y6Mkx/I2Wmsl7gxGsW86AFtSUVGlwmUzA90tyZizGgiYZ9OK65iArg==","repository":{"url":"git+https://github.com/Caputchin/caputchin-sdk.git","type":"git","directory":"presets/kaplay"},"_npmVersion":"11.13.0","description":"Self-contained Caputchin replay preset for the KAPLAY engine: headless boot, fixed-step pump, deterministic RNG rail, and the conforming run/live adapters. The on-ramp for any KAPLAY game on the deterministic-replay platform.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","dependencies":{"@caputchin/determinism":"0.2.0","@caputchin/replay-contract":"0.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","kaplay":"3001.0.19","vitest":"^2.0.0","typedoc":"^0.28.19","happy-dom":"^15.0.0","typescript":"^5.4.0","@types/node":"^22.0.0","@caputchin/game-sdk":"3.1.0","typedoc-plugin-markdown":"^4.11.0"},"peerDependencies":{"kaplay":"3001.0.19","@caputchin/game-sdk":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/preset-kaplay_0.3.0_1780542586936_0.9481182579599234","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@caputchin/preset-kaplay","version":"0.4.0","description":"Self-contained Caputchin replay preset for the KAPLAY engine: headless boot, fixed-step pump, deterministic RNG rail, and the conforming run/live adapters. The on-ramp for any KAPLAY game on the deterministic-replay platform.","keywords":["caputchin","kaplay","kaboom","deterministic","replay","captcha","game","preset"],"homepage":"https://github.com/Caputchin/caputchin-sdk#readme","repository":{"type":"git","url":"git+https://github.com/Caputchin/caputchin-sdk.git","directory":"presets/kaplay"},"bugs":{"url":"https://github.com/Caputchin/caputchin-sdk/issues"},"license":"Apache-2.0","type":"module","publishConfig":{"access":"public"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"sideEffects":false,"dependencies":{"@caputchin/determinism":"0.2.0","@caputchin/replay-contract":"0.3.0"},"peerDependencies":{"@caputchin/game-sdk":"^3.0.0","kaplay":"3001.0.19"},"devDependencies":{"@types/node":"^22.0.0","happy-dom":"^15.0.0","kaplay":"3001.0.19","tsup":"^8.0.0","typedoc":"^0.28.19","typedoc-plugin-markdown":"^4.11.0","typescript":"^5.4.0","vitest":"^2.0.0","@caputchin/game-sdk":"3.1.0"},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","docs":"typedoc && node ../../scripts/normalize-docs.mjs docs"},"_id":"@caputchin/preset-kaplay@0.4.0","_integrity":"sha512-Q4FWkOjmkfcIqxRLESrM1tY2R82N2ErmDlS1yvIxF18B18a7NhuavcROVmbnUKWVIDesTRnZ3f+kOefuZDXXvQ==","_resolved":"/tmp/3428450b75b59ad0d91ebc37992230b5/caputchin-preset-kaplay-0.4.0.tgz","_from":"file:caputchin-preset-kaplay-0.4.0.tgz","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-Q4FWkOjmkfcIqxRLESrM1tY2R82N2ErmDlS1yvIxF18B18a7NhuavcROVmbnUKWVIDesTRnZ3f+kOefuZDXXvQ==","shasum":"3ae3b735e2a8930c9cf0d9374ef1026a129385b4","tarball":"https://registry.npmjs.org/@caputchin/preset-kaplay/-/preset-kaplay-0.4.0.tgz","fileCount":7,"unpackedSize":93010,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGxSumo+t2BM2vEzcMM0bYELsh+rMEkAYVZutg1fsjBzAiA9fEqBzPdajmHyyLyzI0VLBiuIXhjKiVe3VcdAl45fOw=="}]},"_npmUser":{"name":"caputchiner","email":"info@caputchin.com"},"directories":{},"maintainers":[{"name":"caputchiner","email":"info@caputchin.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/preset-kaplay_0.4.0_1780542999003_0.22937939352934555"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-04T01:26:08.203Z","modified":"2026-06-04T03:16:39.365Z","0.2.0":"2026-06-04T01:26:08.527Z","0.3.0":"2026-06-04T03:09:47.092Z","0.4.0":"2026-06-04T03:16:39.207Z"},"bugs":{"url":"https://github.com/Caputchin/caputchin-sdk/issues"},"license":"Apache-2.0","homepage":"https://github.com/Caputchin/caputchin-sdk#readme","keywords":["caputchin","kaplay","kaboom","deterministic","replay","captcha","game","preset"],"repository":{"type":"git","url":"git+https://github.com/Caputchin/caputchin-sdk.git","directory":"presets/kaplay"},"description":"Self-contained Caputchin replay preset for the KAPLAY engine: headless boot, fixed-step pump, deterministic RNG rail, and the conforming run/live adapters. The on-ramp for any KAPLAY game on the deterministic-replay platform.","maintainers":[{"name":"caputchiner","email":"info@caputchin.com"}],"readme":"# @caputchin/preset-kaplay\n\nThe on-ramp for a [KAPLAY](https://kaplayjs.com) game on the\n[Caputchin](https://caputchin.com) deterministic-replay platform.\n\nCaputchin runs a replay captcha: your game records an input trace in the browser,\nand the server re-runs the same simulation over that trace and trusts only the\nreplayed verdict. KAPLAY has no headless mode, so this preset makes it run\nheadless (a DOM/GL shim) AND makes the **full engine deterministic**: it drives\nKAPLAY at a fixed timestep (the same fixed-dt frames in the browser and on the\nserver) and makes `Math` transcendentals deterministic, so KAPLAY's own\n`k.rand()`, physics (`body`, `area`, `move`), and `dt()` reproduce bit-for-bit\nacross both. You write an ordinary KAPLAY game with the full engine; it runs in\nthe browser and re-runs on the server. You give it the conforming `run`.\n\nDeterminism is the preset's job, not yours: write the game with the engine, not\naround it.\n\nDependencies: the mandatory `@caputchin/replay-contract` and the shared\n`@caputchin/determinism` kit (the deterministic Math). Not the engine-kit\nreducer kit.\n\n## Install\n\n```sh\npnpm add @caputchin/preset-kaplay @caputchin/game-sdk kaplay\n```\n\n`kaplay` and `@caputchin/game-sdk` are peer dependencies. Pin the exact `kaplay`\nversion the preset is built against (its determinism is version-specific).\n\n## Write your game once\n\n```ts\n// src/game.ts\nimport { defineKaplayGame } from '@caputchin/preset-kaplay';\n\nexport const game = defineKaplayGame(\n  (k, api) => {\n    // Ordinary KAPLAY, full engine. Physics (body/area/gravity), k.rand, move(),\n    // dt() are all deterministic here. Render-only setup can be skipped on the\n    // server with `if (!api.headless) { ... }`.\n    k.setGravity(1200);\n    const player = k.add([k.rect(20, 20), k.pos(100, 40), k.area(), k.body()]);\n\n    // The sim lives in onFixedUpdate. Read INPUT as named actions (so it can be\n    // recorded + replayed); everything else is plain KAPLAY.\n    let score = 0;\n    k.onFixedUpdate(() => {\n      if (api.isDown('left')) player.move(-120, 0);\n      if (api.isDown('right')) player.move(120, 0);\n      if (api.justPressed('fire') && player.isGrounded()) player.jump(k.rand(300, 600));\n      score += api.justPressed('fire') ? 1 : 0;\n      api.setScore(score);\n      if (score >= readPassScore(api.ctx)) api.pass();\n      if (player.pos.y > 400) api.gameOver();\n    });\n\n    // Touch / on-screen controls inject the same named actions (live only).\n    if (!api.headless) {\n      addTouchButton(k, 'left', () => api.press('left'), () => api.release('left'));\n    }\n  },\n  {\n    actions: ['left', 'right', 'fire'], // index is the trace wire code: APPEND, never reorder\n    keys: { left: ['left', 'a'], right: ['right', 'd'], fire: ['space'] },\n    maxTicks: 60 * 50, // reject runs longer than ~60s (50 ticks/s)\n    kaplay: { width: 320, height: 240, background: [12, 14, 20] },\n  },\n);\n```\n\n## The two entries\n\n```ts\n// src/run.ts  -> caputchin.json `run.entry` (the headless replay artifact)\nimport { kaplayRun } from '@caputchin/preset-kaplay';\nimport { game } from './game.js';\nexport const run = kaplayRun(game);\n```\n\n```ts\n// src/index.ts -> caputchin.json `entry` (the playable browser game)\nimport { register } from '@caputchin/game-sdk';\nimport { mountKaplayGame } from '@caputchin/preset-kaplay';\nimport { game } from './game.js';\nregister((container, bridge, ctx) => mountKaplayGame(game, { container, bridge, ctx }));\n```\n\nBundle both into the iframe artifact (KAPLAY included) with your bundler; there\nis no runtime CDN fetch under the game CSP.\n\n## `caputchin.json`\n\nKAPLAY is pure JavaScript, so the replay artifact is a single entry, no\n`run.modules`:\n\n```json\n{\n  \"entry\": \"dist/<game>.js\",\n  \"run\": { \"entry\": \"dist/run.js\" },\n  \"preferred\": { \"width\": 320, \"height\": 480 }\n}\n```\n\n## What you can use (almost everything)\n\nThe preset makes the engine deterministic, so use it normally:\n\n- **`k.rand()` / `k.randi()` / `k.chance()` / `k.choose()`** - KAPLAY's own seeded\n  RNG. The preset seeds it from the round seed and drives one fixed frame per\n  tick, so it reads the same values in the browser and on the server. (The api\n  also exposes `api.rand`/`randi`/`shuffled` if you prefer.)\n- **`k.shuffle()` / `k.chooseMultiple()` / raw `Math.random()`** - these route\n  through the host `Math.random`, which the preset ALSO seeds (a separate seeded\n  stream, scoped to each frame), so they reproduce both ends too. No longer\n  off-limits - the whole engine RNG surface is yours.\n- **Physics** - `k.setGravity`, `body()`, `area()`, `move()`, collisions. Driven\n  at a fixed `dt`, with deterministic `Math`, so it integrates identically.\n- **`dt()`** - constant (the fixed timestep), so frame-rate-independent movement\n  (`move(speed)`) just works.\n\n## The few rules\n\n- **Input only through `api`** named actions (`isDown` / `justPressed` /\n  `justReleased`), never raw `k.isKeyDown(...)`. This is the one thing the preset\n  must record and replay. Keyboard is wired from `options.keys`; touch/gamepad\n  call `api.press` / `api.release` from live handlers.\n- **No wall clock, no network.** Never read `Date`, `performance.now`, `crypto`,\n  or `fetch` in the sim - those stay non-deterministic. (`Math.random` is the one\n  exception: the preset seeds it, so it and KAPLAY's `shuffle()`/`chooseMultiple()`\n  are safe.)\n- **Load assets through `options.load`** (run before the scene starts), not lazily\n  mid-game, so loading never perturbs the deterministic timeline.\n- **Gate-affecting values come from `api.ctx.config`** (the server-resolved\n  config), never from input.\n\nGet this wrong and your game false-rejects its own players. There is no\nindex-time determinism gate, only a conformance smoke test, so verify locally.\n\n## What the preset handles for you\n\n- Headless KAPLAY boot under an isolated DOM/GL shim, and the fixed-step pump\n  that drives the loop on the server.\n- Seeding KAPLAY's RNG from the round seed.\n- Recording inputs tick-stamped in the browser and replaying them at the same\n  ticks on the server (the trace codec).\n- The `run(seed, config, trace) -> verdict` contract, including a failing verdict\n  for a malformed trace and rejection of a non-terminating run.\n\n## API reference\n\nFull API docs (generated from the source) live in [`docs/`](docs/README.md):\n`defineKaplayGame`, `kaplayRun`, `mountKaplayGame`, the `KaplayGameApi`, and the\nlower-level shim / pump primitives.\n\n## Builds on\n\n- [`@caputchin/replay-contract`](https://www.npmjs.com/package/@caputchin/replay-contract) - the `run` / `Seed` / `Verdict` shapes.\n- [`@caputchin/game-sdk`](https://www.npmjs.com/package/@caputchin/game-sdk) - the widget `register` surface and `ctx`.\n- [`kaplay`](https://kaplayjs.com) - the game engine itself.\n","readmeFilename":"README.md"}