What it does
A meter is one value with a range, an optional drain or fill rate and three edges: low, empty and full. Each edge
fires its callback on the frame the value reaches it and re-arms when the value leaves, so "fuel low" and "out of
lives" happen once, at the right moment, without flags in your game code. spend takes an amount only if it is
there, which is all an ammo counter needs. Named meters sit together on one resources object and step with one
update(dt).
Award rules watch a meter and give something at every multiple of a step or at listed values: an extra life every 10,000 points, a bonus at 50,000. Several thresholds crossed in one frame give several awards, and a meter that drops back below a threshold re-arms it, so a new game that zeroes the score earns its extra lives at the same scores again.
Callouts are what the HUD says. A higher priority interrupts a lower one, a cooldown stops a taunt repeating, and a
when test fires only on its rising edge. The high-score table ranks scores, says whether one qualifies, saves to
JSON and loads back, dropping any invalid rows; the initials wheel takes up, down, left and right from any input.
Player slots each carry their own score, lives and rounds meters, a team and the actor they drive, and next() moves
to the next player with lives left. Because nothing runs on its own, a test can play a whole game in a millisecond.
Features
- Meters — a value with a minimum, an optional maximum, a signed rate per second, a warning level and edge callbacks for low, empty and full.
- Spend and refill — take an amount only if it is there; reset with the edges re-armed.
- Named resources — score, lives, fuel and more on one object, stepped together.
- Award rules — every so many points or at listed values, re-armed when the meter falls back.
- Callouts — text and sound cues with priority, cooldown, a hold time and rising-edge triggers.
- A blink helper —
flashturns a time into on or off at a rate and duty, for a blinking 1UP or a warning lamp. - A high-score table — any size, pre-filled if you like, with placement, qualification, insertion and JSON save and load.
- Initials entry — a letter wheel of any length over any character set.
- Player slots — turns, lives per player, teams, a leader, and switching control to the actor nearest a point.
Good for
- Arcade shooters with a score, a stock of ships, extra lives at set scores and a 1UP that blinks, in the style of Asteroids.
- Games with fuel, ammo or air that drain over time and warn once when low.
- Puzzle and casual games that want only the high-score table and initials entry.
- Hotseat and alternating two-player games that pass the controls when a player loses a life.
- Team sports games, where a player slot switches to whichever team-mate is nearest the ball.
- Headless tests that play whole games without a clock or a screen.
Make it yours
The pack has no options; every builder takes its own definition, which can come from a level file. Meters take a
starting value, range, rate, warning level and callbacks. Awards take the meter to watch, a step or a list of values,
and what to give. Cues take text, a sound, a priority, a cooldown and a hold time, and createCues takes your own
play function. The table takes a size and starting entries; the initials wheel takes a length, a character set and
a starting entry; player slots take a count, starting lives and teams.
Two different uses: a lander game with a fuel meter that drains at four units a second, warns at a quarter with a FUEL LOW callout and ends the game when empty; and a two-player, alternating-turns puzzle game that uses only player slots for turns and the high-score table for a top ten.
A quick look
Score, lives and a draining fuel meter, with an extra life every 10,000 points:
const res = createResources({
score: {},
lives: { value: 3, onEmpty: () => flow.go('over') },
fuel: { value: 100, max: 100, rate: -4, warn: 25, onWarn: () => cues.say('fuel') },
});
res.award({ of: 'score', every: 10000, give: r => r.lives.add(1) });
res.update(dt); // once a frame
if (res.fuel.low) drawGauge(res.fuel.frac);
Works with
wave-plan— what arrives when, difficulty that rises with the level, and what counts as winning.fsm— attract, play, dying and game over as named phases.typography,vector-fontorui— to draw the score and lives these meters hold.save— to keep the high-score table between sessions.
Good to know
- It counts and remembers; it never draws, plays a sound itself or reads input. Your HUD reads it, your input calls
the initials wheel, and cue sounds go through your
playfunction. - Nothing advances on its own: call
update(dt)on resources and cues once a frame. Awards are tested onupdate, not on each write. - The table lives in memory until you save it with
toJSON(). - A meter's rate is applied in one step of
dt, so a meter that must not overshoot inside a long frame needs smaller steps.






