{"_id":"@banjobyster/bysters","_rev":"3-930e7bdfd2df5beec32a3e4aa93016bf","name":"@banjobyster/bysters","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@banjobyster/bysters","version":"0.1.0","keywords":["animation","creatures","dom","pixijs","procedural","webgl","interactive","pathfinding"],"author":{"name":"banjobyster"},"license":"MIT","_id":"@banjobyster/bysters@0.1.0","maintainers":[{"name":"banjobyster","email":"sayan25bak@gmail.com"}],"homepage":"https://banjobyster.github.io/bysters/","bugs":{"url":"https://github.com/banjobyster/bysters/issues"},"dist":{"shasum":"489631c6c3cd6263f28d155b82c82d9d142a2064","tarball":"https://registry.npmjs.org/@banjobyster/bysters/-/bysters-0.1.0.tgz","fileCount":31,"integrity":"sha512-kNTz6rVMmYu/kdsoo4sf7lyJoAY1dOBSEvU4ZDM22R+OXFtlNQuBhVvyFCLHyutcQPNSpa0SEGLAtdk08TFthA==","signatures":[{"sig":"MEUCIG0pr1OVCdGQ6vqxnnEo7HMA7+/FnX6hkdTwKOblvI8zAiEAh8euKVAdilOJqHiXeBA4nt5ZmwP1NYek+8b5AbFM2tk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":177177},"main":"./index.js","type":"module","module":"./index.js","exports":{".":"./index.js","./pixi":"./render/pixi/overlay.js","./behaviors":"./core/behavior/library.js"},"gitHead":"9a3c6b391e3de2037492efae872e5e541eef0ed8","scripts":{"dev":"vite","test":"vitest run","build":"vite build","deploy":"gh-pages -d dist","preview":"vite preview","predeploy":"npm run build"},"_npmUser":{"name":"banjobyster","email":"sayan25bak@gmail.com"},"repository":{"url":"git+https://github.com/banjobyster/bysters.git","type":"git"},"_npmVersion":"11.5.1","description":"Bring a web page to life with bysters: small procedurally-animated creatures that treat the DOM as physical terrain, sense the cursor and each other, and operate stateful elements on the page.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","vitest":"^2.1.9","pixi.js":"^8.0.0","gh-pages":"^6.3.0"},"peerDependencies":{"pixi.js":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/bysters_0.1.0_1783200283003_0.257691800922198","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@banjobyster/bysters","version":"0.2.0","keywords":["animation","creatures","dom","pixijs","procedural","webgl","interactive","pathfinding"],"author":{"name":"banjobyster"},"license":"MIT","_id":"@banjobyster/bysters@0.2.0","maintainers":[{"name":"banjobyster","email":"sayan25bak@gmail.com"}],"homepage":"https://banjobyster.github.io/bysters/","bugs":{"url":"https://github.com/banjobyster/bysters/issues"},"dist":{"shasum":"3f18a5fc52054502b0263e6a8fcf8c8bd85a8000","tarball":"https://registry.npmjs.org/@banjobyster/bysters/-/bysters-0.2.0.tgz","fileCount":31,"integrity":"sha512-Obe5752s9pWkwkPMluQZMTrHblGbH98H96MInBcPsojRYZ6tyy78FVN3fWGMjzRGlSDB2pM2JrPuDNnrNwS10Q==","signatures":[{"sig":"MEYCIQCIeqTX25oDJRvkZXzEcdinqG4glBfAeGM8AIfj6iw5twIhAJSdmdb++3zfBZmuV//SC7CdjIKzUNArAWelCrGtONl9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":185320},"main":"./index.js","type":"module","module":"./index.js","exports":{".":"./index.js","./pixi":"./render/pixi/overlay.js","./behaviors":"./core/behavior/library.js"},"gitHead":"b06b1c456351285c99f8eadce170c70418bb4cb4","scripts":{"dev":"vite","test":"vitest run","build":"vite build","deploy":"gh-pages -d dist","preview":"vite preview","predeploy":"npm run build"},"_npmUser":{"name":"banjobyster","email":"sayan25bak@gmail.com"},"repository":{"url":"git+https://github.com/banjobyster/bysters.git","type":"git"},"_npmVersion":"11.5.1","description":"Bring a web page to life with bysters: small procedurally-animated creatures that treat the DOM as physical terrain, sense the cursor and each other, and operate stateful elements on the page.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.4","vitest":"^2.1.9","pixi.js":"^8.0.0","gh-pages":"^6.3.0"},"peerDependencies":{"pixi.js":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/bysters_0.2.0_1783366447513_0.952292530185548","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@banjobyster/bysters","version":"0.2.1","publishConfig":{"access":"public"},"description":"Bring a web page to life with bysters: small procedurally-animated creatures that treat the DOM as physical terrain, sense the cursor and each other, and operate stateful elements on the page.","type":"module","license":"MIT","author":{"name":"banjobyster"},"homepage":"https://banjobyster.github.io/bysters/","repository":{"type":"git","url":"git+https://github.com/banjobyster/bysters.git"},"bugs":{"url":"https://github.com/banjobyster/bysters/issues"},"keywords":["animation","creatures","dom","pixijs","procedural","webgl","interactive","pathfinding"],"main":"./index.js","module":"./index.js","exports":{".":"./index.js","./behaviors":"./core/behavior/library.js","./pixi":"./render/pixi/overlay.js"},"sideEffects":false,"scripts":{"dev":"vite","build":"vite build","preview":"vite preview","test":"vitest run","predeploy":"npm run build","deploy":"gh-pages -d dist"},"peerDependencies":{"pixi.js":"^8.0.0"},"devDependencies":{"gh-pages":"^6.3.0","pixi.js":"^8.0.0","vite":"^7.0.4","vitest":"^2.1.9"},"_id":"@banjobyster/bysters@0.2.1","gitHead":"a76fc83399cd4669780cb42712cc207b540efff7","_nodeVersion":"24.7.0","_npmVersion":"11.5.1","dist":{"integrity":"sha512-fOYCkPD4mGv8OKCAUGr0OWOADB8XBB46vKj5S3ZP2ProEwnX7u4c6wTHXBFo2oEGd78r4QFdhAofs/sgXCEJ1g==","shasum":"d092d612b7e0430d3a54397c24c39cc46dc9e725","tarball":"https://registry.npmjs.org/@banjobyster/bysters/-/bysters-0.2.1.tgz","fileCount":31,"unpackedSize":188269,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFI1Ftdkbxi8IraT3gQkc2E8Ud+S9pC0Q8oG0KGJyf6zAiEA9A34xhOvclAzNbdNBCXOwVEc6uLVVOHXUKb13Vn1FA4="}]},"_npmUser":{"name":"banjobyster","email":"sayan25bak@gmail.com"},"directories":{},"maintainers":[{"name":"banjobyster","email":"sayan25bak@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bysters_0.2.1_1783368028608_0.5469846456120857"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-04T21:24:42.818Z","modified":"2026-07-06T20:00:28.863Z","0.1.0":"2026-07-04T21:24:43.145Z","0.2.0":"2026-07-06T19:34:07.649Z","0.2.1":"2026-07-06T20:00:28.760Z"},"bugs":{"url":"https://github.com/banjobyster/bysters/issues"},"author":{"name":"banjobyster"},"license":"MIT","homepage":"https://banjobyster.github.io/bysters/","keywords":["animation","creatures","dom","pixijs","procedural","webgl","interactive","pathfinding"],"repository":{"type":"git","url":"git+https://github.com/banjobyster/bysters.git"},"description":"Bring a web page to life with bysters: small procedurally-animated creatures that treat the DOM as physical terrain, sense the cursor and each other, and operate stateful elements on the page.","maintainers":[{"name":"banjobyster","email":"sayan25bak@gmail.com"}],"readme":"# bysters\r\n\r\n**Small procedurally-animated creatures that live on your web page.** They treat\r\nyour real layout as physical terrain, walk it from any angle, sense the cursor and\r\neach other, and operate stateful elements on the page. One import, a list of\r\nbehaviours, and the page starts playing itself.\r\n\r\n**[Live demo](https://banjobyster.github.io/bysters/)** (scroll down, and hover the\r\nlittle bots)\r\n\r\n![The bysters landing page: a little bot has walked across the floor to switch a beacon back on](docs/hero.webp)\r\n\r\n```js\r\nimport { mount, behaviors } from '@banjobyster/bysters'\r\nconst { wander, followCursor, flee } = behaviors\r\n\r\nawait mount({\r\n  terrain: '[data-walk]',                 // elements they can stand and climb on\r\n  bysters: [\r\n    { name: 'pip',  character: Pip,  behaviors: [followCursor(), wander()] },\r\n    { name: 'byte', character: Byte, behaviors: [flee(v => v.name === 'pip'), wander()] },\r\n  ],\r\n})\r\n```\r\n\r\nThat is the whole setup. Mark a few elements with `data-walk`, hand `mount()` your\r\ncast, and they wake up.\r\n\r\n## What it is\r\n\r\nA byster is a **character** (its look and tuning) plus a **mind**: a short, ordered\r\nstack of small, composable behaviours. Each frame a byster:\r\n\r\n1. senses a read-only snapshot of the world (the cursor, the other bysters and their\r\n   advertised state, the walkable graph), then\r\n2. every behaviour bids **per channel** (locomotion, gaze, face, pace, appearance,\r\n   interact), and the highest-priority bid wins each channel independently, so gaze,\r\n   face and movement can be driven by three different behaviours at once, then\r\n3. a motor executes the winning intents and smooths them into motion.\r\n\r\nThe framework is **value-neutral**: it has no notion of good or bad, fix or fail,\r\nhero or villain. All of that meaning lives in your config and your CSS. The demo's\r\n\"green is fixed, red is broken\" is entirely CSS on the consumer side; the framework\r\nonly ever moves opaque state between opaque states.\r\n\r\n## Install\r\n\r\n```sh\r\nnpm install @banjobyster/bysters pixi.js\r\n```\r\n\r\n`pixi.js` (v8) is a peer dependency: bysters renders through it.\r\n\r\n## Core ideas\r\n\r\n### The DOM is the terrain\r\n\r\nMark anything walkable with `data-walk`. A plain `data-walk` is a top surface; opt\r\ninto other faces with `data-walk=\"top bottom left right\"` and a byster will scale the\r\nwalls and hang under the undersides. bysters compiles those rectangles into a surface\r\ngraph with two movement primitives, a **walk** (along a surface or around a shared\r\ncorner) and a single **ballistic arc** (every hop, drop and leap is the same jump\r\nunder gravity). It re-plans the whole graph when the layout changes.\r\n\r\n```html\r\n<div data-walk style=\"...\">a floor</div>\r\n<div data-walk=\"top bottom left right\" style=\"...\">a box they circle</div>\r\n```\r\n\r\n### Behaviours compose\r\n\r\nA personality is just a list. The built-in library (`import { behaviors }` or\r\n`import { ... } from '@banjobyster/bysters/behaviors'`) includes:\r\n\r\n| behaviour | what it does |\r\n| --- | --- |\r\n| `wander()` | amble to a random reachable spot |\r\n| `followCursor()` / `fleeCursor({ alpha })` | seek or bolt from the pointer (bolt can turn glassy) |\r\n| `watchCursor()` / `watchNearest()` / `avoidCursorGaze()` | where it looks |\r\n| `approach(pred, { standoff })` / `flee(pred)` | chase or run from a matching byster (route-aware); `standoff` px makes it an escort that settles BESIDE its target, never on top of it |\r\n| `caughtBy(pred)` / `reactTo(pred)` | freeze when caught, or react to another's state |\r\n| `operateFixtures({ match, drive })` | drive a stateful element toward a target state |\r\n| `perch()` / `sleep()` / `mood()` / `flourish()` / `liveliness()` | idle life |\r\n| `fatigue(inner)` / `sometimes(inner)` / `group(...inner)` | higher-order wrappers: tire it out, gate it on a coin flip, or fuse several into one unit (later bids win overlapping channels) |\r\n\r\nBehaviours are **pure**: given the world and themselves, they return intents. They\r\nnever touch each other. Adding a byster edits no other byster.\r\n\r\n### Bysters sense each other\r\n\r\nInteraction is decentralised. A byster reacts to the others from its own behaviour\r\ncode, through `world.bysters` and each other's read-only `view()`\r\n(`{ name, x, bodyY, surface, state, caps, tags, face }`). A byster can **broadcast tags**\r\nthat others sense, so \"the cop is tired\" or \"the imp is caught\" is just a string one\r\nbyster advertises and another reads:\r\n\r\n```js\r\n// the imp only fears the cop while the cop is not resting\r\nflee(v => v.name === 'cop' && !v.tags.has('winded'))\r\n```\r\n\r\n![A switchback stair where one bot chases another: nothing is scripted, the chase comes out of each bot's own behaviour list](docs/chase.png)\r\n\r\n### Fixtures: value-neutral stateful elements\r\n\r\nMark stateful elements with `data-fixture` and give them opaque states. A byster's\r\n`operateFixtures` behaviour routes to one and transitions it through a guarded store.\r\nMirror the same behaviour with opposite config and you get a rivalry (one drives\r\nthings to `broken`, another to `fixed`) with no special-cased \"saboteur\" or \"repairer\"\r\ntype anywhere in the code. The store mirrors state onto `data-state` for your CSS.\r\n\r\n## API\r\n\r\n```js\r\nimport { mount, behaviors } from '@banjobyster/bysters'\r\n```\r\n\r\n### `mount(config) -> handle`\r\n\r\nAll keys optional except that each byster needs a `character`.\r\n\r\n| key | meaning |\r\n| --- | --- |\r\n| `terrain` | selector for walkable elements (default `'[data-walk]'`) |\r\n| `fixtures` | selector for fixtures, or `false` to disable (default `'[data-fixture]'`) |\r\n| `ground` | synthesize a viewport-bottom floor (default `true`; `false` for tall multi-scene pages that bring their own floors) |\r\n| `shadow` | draw the ground shadow under each byster (default `true`) |\r\n| `bysters` | `[{ name, character, caps?, behaviors?, planner?, spawn? (ground fraction) or spawnAt? (selector), speedScale?, alpha?, tint?, shadow? }]` |\r\n| `onFrame(frame)` | per-frame hook for consumer-drawn extras (a plug cable, an overlay) |\r\n| `onPointerDown(worldPoint, event, handle)` | pointer hook |\r\n| `space` | injected coordinate provider (default `DocumentSpace`; inject a `FixedSpace` for headless/deterministic runs) |\r\n\r\nThe handle exposes `stage`, `store`, `graph`, `cast`, `byName(name)`, `goto(name, el)`,\r\n`on(event, cb)`, `step(seconds)` (deterministic stepping for tests) and `unmount()`.\r\nUnder `prefers-reduced-motion` or without WebGL, `mount()` resolves to a degraded\r\nno-op handle and leaves the page untouched, so callers never have to branch.\r\n\r\n### Bring your own character\r\n\r\nA character is plain data: params (size, gait, springs), a palette, a pixel-face\r\ndefinition, and `buildBody`/`buildHead` draw functions. The demo ships four\r\n(a toddler, a glitch imp, a sergeant, a drifter); none of them import anything, so\r\nthey are easy to copy and retune. Use `caps: { maxLaunch, gravity }` to make one\r\nbyster leap harder or floatier than the rest.\r\n\r\n## Develop\r\n\r\n```sh\r\nnpm install\r\nnpm run dev        # the demo at /demo, on a dev server\r\nnpm test           # the pure core, headless under vitest\r\nnpm run build      # build the demo to ./dist\r\n```\r\n\r\nThe pure core (`core/`) imports no `document`, `window` or Pixi, so it runs and is\r\ntested entirely headless. DOM access lives only in `dom/`, Pixi only in `render/`.\r\n\r\n## License\r\n\r\nMIT. Creativity by [banjobyster](https://github.com/banjobyster).\r\n","readmeFilename":"README.md"}