One controller for keys, pointer and a touch stick, mapped to named intents. Listeners only record; the engine's read phase latches edges and measures the target, so a step sees a stable snapshot. Keys typed into the host page's fields (or inside [data-fizlet-ignore]) never count. Held state is dropped on window blur, when the engine pauses, on dispose() and on destroy: nothing stays stuck. hit (an intent or the pointer): a press is seen by exactly one fixed step, the first step after it, even when that comes frames later (0-step frames while catching up) and never again in the same frame's later steps. Outside a step (read, render), hit is true for the one frame whose read latched it. Presses between two steps count once.
fizlet/input · GZIP1,283 B1.3 KB of a 1.5 KB budget
Measured on this build: minified and gzipped with everything it imports, the way npm run size checks the budget in CI.
MODULES · hosted on this site
import { controls } from "https://coded-dot-design.github.io/fizlet/dist/input.js";
NPM · COMING SOON
npm install fizlet # not published yet
import { controls } from "fizlet/input";
IN SHORT
const pad = controls(e, { keys: { fire: ["Space"] } });
if (pad.hit("fire")) fire(); // true for one frame
if (pad.pointer.hit) aim(pad.pointer.x, pad.pointer.y);
API
Every export, with its doc comment, read from the published type declarations of v0.10.0.
ControlsOptions interface
export interface ControlsOptions {
/** intent → key codes, e.g. { fire: ["Space"], up: ["KeyW", "ArrowUp"] } (KeyboardEvent.code). */
keys?: Record<string, string[]>;
/** Where the pointer is measured and the stick lives (default: the engine root, else window). */
target?: HTMLElement;
/** Touch: a drag that starts on the left half (or anywhere: "any") drives a virtual stick. */
stick?: "left" | "any" | false;
/** Stick radius in CSS px (default 60). */
radius?: number;
}
controls function
export declare function controls(e: Engine, o?: ControlsOptions): {
/** Held right now (any of its keys, or press()). */
down: (intent: string) => boolean;
/** Pressed: true in the first step after the press; outside steps, for the frame it latched. */
hit: (intent: string) => boolean;
/** -1..1 from two intents (keys), plus the stick when it's active. */
axis(neg: string, pos: string, stickAxis?: 0 | 1): number;
pointer: {
readonly x: number;
readonly y: number;
readonly down: boolean;
readonly hit: boolean;
};
stick: {
readonly x: number;
readonly y: number;
readonly active: boolean;
};
/** Press an intent from code (on-screen keys): `press("strike")`. */
press(intent: string, down?: boolean): void;
/** Drive the stick from code (an on-screen stick, fizlet/pad): x, y in -1..1; `on = false` lets go. */
steer(x: number, y: number, on?: boolean): void;
dispose: () => void;
system: System;
};
Controls type
export type Controls = ReturnType<typeof controls>;