rpi-animations
v1.1.6
Published
rpi-animations
Readme
rpi-animations
Tiny, event-driven building blocks for running LED/pixel animations in Node.js. The documentation is generated by ChatGPT given the complete code.
The package exposes two classes:
Animation— a base class that provides a render loop (frequency-capped), lifecycle handling (start/stop), duration/iteration limits, cancellation, and event hooks.AnimationQueue— a simple FIFO runner that executes animations one after another and emitsidlewhen the queue is empty.
Under the hood,
Animationuses thesleepmodule and Node’s event system (events).
Install
npm install rpi-animationsQuick start
1) Create your own animation
Extend Animation and implement render(). You can use renderFrequency, duration, or iterations to control the loop.
const { Animation } = require('rpi-animations');
class Blink extends Animation {
constructor(opts = {}) {
super({ name: 'Blink', renderFrequency: 100, ...opts }); // 10 FPS
this.on = false;
}
render() {
// Called by the loop at most once per renderFrequency
this.on = !this.on;
// ...write pixel buffer or call your renderer here...
// e.g. ws281x.render(this.on ? redBuffer : blackBuffer)
this.debug('Blink render', this.on);
}
}
// Run for ~2 seconds then stop
const anim = new Blink({ duration: 2000, debug: true });
anim.run().then(() => console.log('Blink finished'));2) Queue multiple animations
const { Animation, Queue } = require('rpi-animations');
class Fade extends Animation {
constructor(opts = {}) {
super({ name: 'Fade', renderFrequency: 33, iterations: 60, ...opts }); // ~30 FPS, 60 frames
this.i = 0;
}
render() {
// compute brightness from this.i (0..59)
const t = this.i / (this.iterations - 1);
// ...render with t...
this.i++;
}
}
const q = new Queue({ debug: true });
q.on('idle', () => {
console.log('Queue is idle');
});
// Enqueue a few animations
q.enqueue(new Fade({ name: 'Intro' }));
q.enqueue(new Fade({ name: 'Outro' }));Concepts
Animation loop
Animation maintains an internal loop that:
- Calls your
render()no more often thanrenderFrequency(ms). - Stops automatically when any of these conditions are met:
cancel()was called,durationelapsed (ms),iterationsreached (number of frames rendered — increased after each successfulrender()).
Timing
renderFrequency(number, ms): maximum render rate. If omitted,render()can be called as fast as the loop runs.duration(number, ms): total time to keep looping before resolving.iterations(number): how many times to render before resolving.
You can pass timing both in the constructor and to
run({ duration, priority }). If supplied torun, they temporarily override the instance values.
Cancellation
Call cancel() on an animation to request a stop. The loop observes the flag and resolves the pending run() promise. Several events fire (see below).
Priority
Animation instances have a priority property (defaults to 'normal'). The included Queue currently executes animations in FIFO order and does not re-order by priority. The field is available for consumers that want to build a priority-aware scheduler.
API
Class: Animation (extends EventEmitter)
Constructor
new Animation({
name = 'Noname',
renderFrequency, // number (ms), optional
iterations, // number, optional
duration, // number (ms), optional
priority = 'normal',
debug // boolean | function, optional
})debug:- If a function, it will be called for internal logs.
- If truthy (e.g.
true), logs go toconsole.log. - Otherwise, logging is disabled (no-op).
Methods
render()→void
Implement in subclasses. Called by the loop at most once perrenderFrequency.run(options?)→Promise<void>
Starts the animation (start()), runs the loop, then callsstop(). Optionally override{ duration, priority }.start()/stop()→Promise<void>
Lifecycle hooks that emit events (started,stopped). Default implementations just resolve and emit.cancel()→void
Requests cancellation and emits (cancelling,canceling) immediately. The loop then emits (cancelled,canceled) when observed.sleep(ms)→void
Synchronous sleep usingSleep.msleep(ms). You normally don’t need this; the render cadence is handled internally.next(loop)→void
Schedules the next iteration viasetImmediate(loop). Override if you want a different scheduling strategy.loop()→Promise<void>
Internal loop that throttles calls torender()and resolves on cancel/duration/iterations.
Properties
namestringprioritystringduration?numberiterations?numberrenderFrequency?numberiterationnumber— internal counter (increments after each render wheniterationsis set)cancelledboolean— internal flagrenderTime?Date— last render timestampdebugfunction— logger (no-op by default)
Events
Emitted by an Animation instance:
started— afterstart()resolves.stopped— afterstop()resolves.cancelling/canceling— whencancel()is called.cancelled/canceled— when the loop noticescancelled === trueand resolves.
Both British and American spellings are emitted for convenience.
Class: Queue (extends EventEmitter)
A minimal FIFO scheduler that runs animations sequentially.
Constructor
new Queue({ debug }) // debug works like in Animation (fn | truthy | falsy)Methods
enqueue(animation)→void
Adds anAnimationinstance to the queue. If the queue was idle, it starts processing.dequeue()→Promise<void>
Internal: runs the head animation viaanimation.run()and recurses until the queue is empty.run()→Promise<void>
Convenience alias that begins processing (equivalent to callingdequeue()).
Events
idle— emitted when the queue becomes empty after processing all animations.
Integration tips
- Combine
rpi-animationswith your pixel rendering layer (e.g.rpi-ws281xor your own driver). Keep drawing insiderender(); store any shared state on the instance. - Prefer
iterationsfor frame-counted animations (fade across N frames), anddurationfor time-based ones (run for T milliseconds). - If you need a different scheduling (e.g.
setTimeoutat a fixed step), overridenext(loop).
Example: Blink + Fade with a Queue
const { Animation, Queue } = require('rpi-animations');
class Blink extends Animation {
constructor(opts = {}) {
super({ name: 'Blink', renderFrequency: 100, iterations: 20, ...opts });
this.on = false;
}
render() { this.on = !this.on; }
}
class Fade extends Animation {
constructor(opts = {}) {
super({ name: 'Fade', renderFrequency: 33, iterations: 60, ...opts });
this.i = 0;
}
render() { this.i++; }
}
const q = new Queue({ debug: (...a) => console.log('[queue]', ...a) });
q.on('idle', () => console.log('All animations done'));
q.enqueue(new Blink());
q.enqueue(new Fade());License
ISC
