Dunky: a state machine built for performance
StateβMachine
Define states once.<br>Render it anywhere.<br>A state-machine for design systems and complex UIs.
Blazing fast sealed in pure states and events. The same behavior drops<br>into any JS renderer. Inspired by<br>XState<br>and<br>Zag.
Get started β GitHub<br>$ npm i @dunky.dev/state-machine
Why Dunky<br>π Locked in by design
No external prop, no callback, no handle into the machine.<br>Its behavior is closed to the world. Consumers react to the<br>machine from outside.
π Take it anywhere
The machine carries a universal, interactive UI of its own,<br>clickable on any JS surface, precisely because it's a closed<br>box.
β‘οΈ Blazing fast
Design systems and complex UIs can run hundreds of live<br>machines at once. Dunky is tuned for exactly that load. See the benchmark β
Fast machines
Few bytes of state, events and context per machine. A tiny<br>footprint can spin up thousands of them
Built for high density Γ high frequency .
(Expressive<br>Code) so the syntax theme matches the docs everywhere. --> // π‘ pacman: eats until the ghost gets him
const pacman = machine({
initial: 'eating',
context: { x: 1, y: 1, dir: 'right', mouth: 'open' },
states: {
eating: {
on: {
step: {
actions: act($ => ({
x: $.event.x,
y: $.event.y,
})),
},
die: { target: 'dead' },
},
},
dead: {
on: {
revive: { target: 'eating' },
},
},
},
})
// π» ghost: chases on each tick, stops on a catch
const ghost = machine({
initial: 'roaming',
context: { x: 11, y: 10, dir: 'up' },
states: {
roaming: {
on: {
tick: {
actions: act($ => chase($.context, $.event)),
},
stop: { target: 'stopped' },
},
},
stopped: {
on: {
reset: { target: 'roaming' },
},
},
},
})
// π board: dots, cherry, score
const board = machine({
initial: 'playing',
context: { dots, cherry, score: 0 },
states: {
playing: {
on: {
eat: {
actions: act($ => scoreAt($.context, $.event)),
},
caught: { target: 'caught' },
},
},
caught: {
on: {
reset: { target: 'playing' },
},
},
},
})
// β±οΈ a clock machine self-drives via `after`: no external loop
const clock = machine({
initial: 'running',
states: {
running: {
after: {
200: { target: 'running' },
},
},
},
})
// π² compose the four; sync() fans each beat to all regions in order
const game = compose({ clock, pacman, ghost, board })
game.sync(() => {
const { x, y } = step(pacman.context)
pacman.send({ type: 'step', x, y })
board.send({ type: 'eat', x, y })
ghost.send({ type: 'tick', targetX: x, targetY: y })
})
game.start()
({ x: $.event.x, y: $.event.y, })), }, die: { target: 'dead' }, }, }, dead: { on: { revive: { target: 'eating' }, }, }, },})// π» ghost: chases on each tick, stops on a catchconst ghost = machine({ initial: 'roaming', context: { x: 11, y: 10, dir: 'up' }, states: { roaming: { on: { tick: { actions: act($ => chase($.context, $.event)), }, stop: { target: 'stopped' }, }, }, stopped: { on: { reset: { target: 'roaming' }, }, }, },})// π board: dots, cherry, scoreconst board = machine({ initial: 'playing', context: { dots, cherry, score: 0 }, states: { playing: { on: { eat: { actions: act($ => scoreAt($.context, $.event)), }, caught: { target: 'caught' }, }, }, caught: { on: { reset: { target: 'playing' }, }, }, },})// β±οΈ a clock machine self-drives via `after`: no external loopconst clock = machine({ initial: 'running', states: { running: { after: { 200: { target: 'running' }, }, }, },})// π² compose the four; sync() fans each beat to all regions in orderconst game = compose({ clock, pacman, ghost, board })game.sync(() => { const { x, y } = step(pacman.context) pacman.send({ type: 'step', x, y }) board.send({ type: 'eat', x, y }) ghost.send({ type: 'tick', targetX: x, targetY: y })})game.start()">
machine state context βοΈ pacman β¦ βοΈ ghost β¦ βοΈ board β¦
One machine.
Rendered on any JS runner.
The machine is a pure kernel with no environment assumptions. Plug it into any JS UI. If JS runs it,<br>the machine drives it .
machine state context βοΈ pacman β¦ βοΈ ghost β¦ βοΈ board β¦<br>ON / CH-03<br>dunky-dev.com
Web
Devices
Terminals
I spent a decade rebuilding<br>the same components,<br>over and over again.
New company, new stack, new design system, but the same button,<br>the same tooltip, the same dialog. The behaviors never changed.<br>All documented by the W3C. All rebuilt from scratch, every time.
Read the full story β
The stateful<br>machines lab.
Different domains. Same engine.
State, context, events. That's it.
Any stateful UI, is just a machine with states and transitions. The<br>rendering layer is irrelevant.
π Trading panel watching<br>USDEUR β β USDJPY β β BTCUSD β β<br>β +0.00<br>position β<br>BUY SELL CANCEL
machine code βΈ const { createMachine } = setup.asPairContext, PairEvent, PairComputed>().config({
guards: {
canBuy: $ => $.context.entryPrice === 0,
canSell: $ => $.context.entryPrice > 0,
},
actions: {
recordEntry: act($ => ({ entryPrice:...