bndr-js
v1.0.0
Published
RxJS-based composable user input streams (keyboard, pointer, gamepad, MIDI) with glyph metadata
Downloads
2,703
Readme
Doc ⌇ Sandbox ⌇ API ⌇ Become a Sponsor
Bndr is an RxJS-based library for composing user-input streams from devices such as mice🖱️, styluses🖊️, touch inputs👆, keyboards⌨️, MIDI controllers🎹, and gamepads🎮. It provides device sources as plain RxJS Observables, plus a few input-specific operators and combinators that the standard RxJS library doesn't cover. Developed and maintained by Baku Hashimoto.
Potential use cases:
- ⚡️ Associating user inputs with arbitrary triggers for VJing
- 🎨 Introducing manual operations in generative art
Supported devices
- 👆 Pointer (mouse, stylus, touch) — all
PointerEventparameters (pressure, tilt, multi-touch) - ⌨️ Keyboard
- 🎹 MIDI — CC and velocity
- 🎮 Gamepad — vendor-specific button names (JoyCon, PS5, Xbox)
Install
npm install bndr-js rxjsrxjs is a peer dependency — install it alongside.
Example
import {Keyboard, Pointer, Gamepad, Midi} from 'bndr-js'
import {merge, cascade} from 'bndr-js/combinators'
import {rising, lerp} from 'bndr-js/operators'
import {throttleTime} from 'rxjs'
import {vec2} from 'linearly'
// Each device exposes pipeable Observables.
Pointer.pressed().subscribe(pressed =>
console.log('Pointer %s', pressed ? 'pressed' : 'released')
)
// Smooth a pointer position with rAF-based lerp.
Pointer.position()
.pipe(lerp(vec2.lerp, 0.1))
.subscribe(([x, y]) => console.log('Pointer moved: [%f, %f]', x, y))
// Keyboard shortcuts.
Keyboard.shortcut('shift+c').subscribe(() => console.log('shift+c'))
Keyboard.pressed('a').subscribe(p => console.log(`Key 'a' ${p ? 'down' : 'up'}`))
// MIDI velocity from channel 0, note 50.
Midi.note(0, 50).subscribe(v => console.log('MIDI #50: %d', v))
// Gamepad analog stick.
Gamepad.axis(0).subscribe(([x, y]) => console.log('Axis 0: [%f, %f]', x, y))
// Mix bang sources, preserving the glyph metadata for visualization UIs.
merge(Keyboard.shortcut('cmd+s'), Gamepad.button('a').pipe(rising())).subscribe(save)
cascade(
Keyboard.pressed('a'),
Keyboard.pressed('b'),
Keyboard.pressed('c'),
).subscribe(t => console.log(t ? 'a → b → c held' : 'released'))
// Mix freely with standard RxJS operators.
Keyboard.shortcut('cmd+s')
.pipe(throttleTime(500))
.subscribe(save)Tree-shakable subpath imports
Each device and operator group is also published as a subpath, so bundlers can drop unused devices entirely:
import * as Keyboard from 'bndr-js/keyboard'
import {lerp} from 'bndr-js/operators'
import {merge} from 'bndr-js/combinators'Glyph metadata
Sources return a GlyphedObservable<T> — an Observable<T> with a glyph: Glyphs property describing the input visually. Each glyph is either an iconify reference or a text literal, so a sequence like [⌘, "S"] can mix iconography with letter characters. Useful for "press cmd+S to save" tooltips and command palettes. Once you pipe() through arbitrary RxJS operators, the glyph is dropped and the result is a plain Observable. The glyph-aware combinators in bndr-js/combinators (merge, combineLatest, cascade) join the glyphs of their inputs and shadow the same-named RxJS exports when both are imported.
const s = Keyboard.shortcut('cmd+s')
s.glyph // → [{type: 'iconify', icon: 'mdi:apple-keyboard-command'}, 'S']License
MIT. See the included LICENSE file.
