npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kubohiroya/turbowarp-async-input

v0.11.0

Published

A target-scoped asynchronous keyboard, pointer, and accumulated pose input extension for TurboWarp.

Readme

TurboWarp-Async-Input

English | 日本語

A target-scoped asynchronous keyboard, pointer, and accumulated pose input extension for TurboWarp Temporary Variables.

See the illustrated English user guide or the Japanese user guide for setup, examples, listener ownership, and troubleshooting.

Minimal demo

Open in TurboWarp Editor · Download SB3

Click the green flag, click the stage to focus it, then press Space: the counter increases by one and the runtime value becomes pressed. S unregisters the binding; the Stop button also releases it. A new green flag resets the count and registers once. Editing a text field does not trigger the binding. The SB3 declares official Temporary Variables as a dependency, so loading it requires network access. No camera, microphone, or external device is needed.

See samples/README.md for editable sources and regeneration.

Installation

Build or download dist/async-input.js, then load it as a local custom extension in TurboWarp Desktop with Run extension without sandbox enabled. Load TurboWarp's Temporary Variables extension before registering input. Pose input also requires TurboWarp TM with its accumulated pose scoring and change event features enabled.

The versioned npm package contains the reviewed build:

pnpm add --save-exact @kubohiroya/[email protected]

Load node_modules/@kubohiroya/turbowarp-async-input/dist/async-input.js, or use the version-pinned CDN URL:

https://cdn.jsdelivr.net/npm/@kubohiroya/[email protected]/dist/async-input.js

The distributed build enables key and touch blocks through the compile-time asyncInput feature flag in config/feature-flags.ts. Pose blocks additionally require the independently reversible poseInput feature flag, which remains OFF by default.

Composition API

DSL controllers can import @kubohiroya/turbowarp-async-input/composition without registering the Standalone extension or accessing global Scratch, the DOM, or the TurboWarp VM. The caller injects only the event sources it uses:

import {createAsyncInputComposition} from '@kubohiroya/turbowarp-async-input/composition';

const input = createAsyncInputComposition({
  poseSource: tmComposition,
  keySource,
  actorTouchSource,
  qrSource,
  nfcSource,
});
const selectedPose = await input.waitForPoseCandidate({
  candidates: ['help', 'jump'],
  signal,
});
const selectedKey = await input.waitForKeyCandidate({
  candidates: ['Space', 'ArrowRight'],
  signal,
});
const selectedActor = await input.waitForActorTouchCandidate({
  candidates: ['LeftDoor', 'RightDoor'],
  signal,
});
const selectedQr = await input.waitForQrCandidate({
  candidates: ['qr:next'],
  cameraId: 'qr',
  signal,
});
const selectedNfc = await input.waitForNfcCandidate({
  candidates: ['0123456789ABCDEF'],
  readerId: 'right',
  signal,
});

waitForPoseCandidate does not poll the pose source's current value. It subscribes only to version 1 accumulated-pose changes after the wait starts and resolves with exactly one registered candidate. Before subscribing, it calls poseSource.resetAccumulatedPose() so every accepted wait starts a new selection session with zero accumulated score. Empty poses and non-candidates are ignored. Invalid events reject the wait without selecting a pose.

keySource publishes version 1 key observations. Its code is the exact KeyboardEvent.code; it also reports repeat, IME composition, Shift/Ctrl/Alt/Meta modifier, and interactive-focus state. The composition ignores any observation with one of those exclusion flags, then resolves the first code present in candidates. The source must not prevent browser defaults or stop event propagation.

actorTouchSource publishes version 1 renderer-canvas pointer observations. It resolves the topmost drawable to a DSL actor ID by exact, unique actorName matching and reports primary-button, topmost-pick, and uniqueness flags. The composition ignores an observation unless all three flags are true, then resolves the first actor ID present in candidates.

qrSource provides waitForQrText({cameraId, signal}), typically from @kubohiroya/turbowarp-jsqr. cameraId selects a named Camera Source stream such as qr. nfcSource provides waitForNfcIdm({readerId, signal}), typically from @kubohiroya/turbowarp-webusb-pasori. readerId selects a named PaSoRi such as left or right. The composition accepts only values present in candidates; non-candidate scans keep waiting until the source returns another value or the signal is aborted.

Pose, key, actor-touch, QR, and NFC waits share latest-wins ownership within one composition instance. Starting a new valid wait in any mode first unsubscribes and rejects the previous wait with an AbortError; stale events cannot resolve either wait. An already-aborted, invalid, or missing-source request never subscribes and does not replace the current valid wait. Empty and duplicate candidates are rejected. Abort, resolution, rejection, and releaseAll() all unsubscribe immediately.

This API implements only async-input candidate selection. A DSL controller must keep it mutually exclusive with an Actor ordered-pose sequence. If they conflict, the controller cancels the candidate wait and gives the Actor sequence priority. The two modes do not share score or progress state. Scene movement, rewind, stop, live reload, and runtime release must abort the action signal; re-entering the same scene then starts another zero-score selection session. Standalone blocks, opcodes, and the default-OFF poseInput feature flag are unchanged.

Extension ID compatibility

Two independent ID migrations affect this extension. Async Input's own ID changes the opcodes stored in project files. The TurboWarp TM ID that accumulated pose input resolves at runtime changes nothing in project files. Neither migration implies the other.

Async Input's own extension ID

This migration release uses the standards-compliant ID kubohiroyaasyncinput. Existing projects that store twAsyncInput opcodes must apply a schema-aware project migration at the same time; replacing the JavaScript artifact alone would break their existing blocks.

TurboWarp TM extension ID

Accumulated pose input resolves TurboWarp TM through a runtime extension ID. Async Input 0.6.0 moved that lookup from TurboWarp TM's 1.x ID to kubohiroyatm, which TurboWarp TM adopted in 2.0.0. Only matching sides resolve each other:

| Async Input | TurboWarp TM ID it resolves | TurboWarp TM 1.x | TurboWarp TM 2.0.0 and later | | --- | --- | --- | --- | | 0.5.x and earlier | the 1.x ID | supported | rejected | | 0.6.0 and later | kubohiroyatm | rejected | supported |

The 1.x ID is not spelled out here, because this repository keeps legacy pose-era naming out of its documentation. Consult TurboWarp TM 1.x's own documentation when that value is needed. Every release from 0.6.0 onward, including the current one, pairs with TurboWarp TM 2.0.0 or later.

A rejected combination withholds accumulated pose input and nothing else. Keyboard, touch, QR, and NFC input never consult TurboWarp TM and behave identically. The failure is not silent either: registration requires a TurboWarp TM extension that reports accumulated pose event support, so an unresolved ID surfaces as a registration failure rather than a binding that never fires.

The poseInput feature flag is off by default in config/feature-flags.ts. A build that leaves it off never reaches this lookup, so the combination cannot matter there.

Extension ID and event version are separate compatibility axes. Pose input subscribes to TM_ACCUMULATED_POSE_CHANGED version 1, and a matching extension ID does not by itself guarantee a matching event version. repo-policy.json holds integrations.turboWarpTMExtensionId and integrations.accumulatedPoseEvent as the source of truth for both.

Runtime variable initialization

Listen blocks only register or replace input bindings. Registering or removing a listener does not create, clear, or otherwise initialize its runtime variable. Initialize the variable explicitly with Temporary Variables before registering listeners whenever the project requires a predictable starting state. Use an empty string for an initially unset input value, or a finite number such as 0 before using +, -, *, or / compound assignment.

Initialize a shared runtime variable once before registering all targets that write to it. This avoids one target's registration resetting values already used by another target.

Target ownership

Every binding belongs to the sprite, clone, or stage that executes the registration block. Key bindings are identified by the current target ID and KeyboardEvent.code. Ordinary touch bindings are identified by the current sprite or clone target ID. The actor-touch compatibility block keeps the executing target as owner while binding the pointer hit to the resolved actor target. Two clones of the same sprite can therefore register independent bindings.

The extension uses one window keydown listener, one renderer-canvas pointerdown listener, and one TurboWarp TM runtime-event listener regardless of the number of bindings. Target deletion removes that target's bindings. Green flag, project stop, and runtime disposal remove all bindings.

Keyboard input

Key IDs use KeyboardEvent.code, for example KeyA, Digit1, ArrowLeft, Space, or Enter. Multiple targets may listen for the same key without replacing each other. Re-registering the same key from the same target replaces only that target's binding.

Repeated keydown events, IME composition, and events from input, textarea, select, or editable elements are ignored. The extension does not prevent browser defaults or stop propagation.

Touch input

The ordinary touch registration block always refers to the sprite or clone that executes it. Stage targets are rejected. The renderer's topmost pick result is matched by target ID, so original sprites and clones remain distinct and transparent pixels follow TurboWarp renderer behavior.

For kamishibai DSL integration, listenForActorTouchAndBroadcast resolves a non-stage target whose actorName variable exactly matches the supplied actor name. Missing and duplicate actor names are rejected. This compatibility block is maintained outside the generated public block reference.

Broadcast after input

The key and touch and broadcast variants update the runtime variable first, then start the matching standard Scratch broadcast without waiting for its receiver scripts to finish. The message name is trimmed and must not be empty. If the runtime variable update fails, including an invalid compound arithmetic update, the broadcast is not started. Each matching target-owned key binding starts its own configured broadcast.

Accumulated pose input

The pose registration block listens for TurboWarp TM's TM_ACCUMULATED_POSE_CHANGED version 1 event. It runs only when the selected accumulated pose name changes to the registered name; confidence changes that keep the same accumulated pose do not retrigger it. Leaving a pose and later returning to it triggers the binding again.

Bindings are keyed by the executing target ID and pose name. Multiple targets can listen for the same pose independently, and one target can listen for multiple poses. Registration fails without Temporary Variables or a TurboWarp TM extension that reports accumulated pose event support.

Compound arithmetic

A value beginning with +, -, *, or / performs numeric compound assignment against the runtime variable's latest value:

+2   -> current += 2
-1   -> current -= 1
*3   -> current *= 3
/2   -> current /= 2

Operands must be finite numbers. Missing or non-numeric current values, division by zero, overflow, and other non-finite results leave the current value unchanged. Values without an arithmetic prefix, including an empty value, use exact string assignment.

DSL integration

Actor-aware touch routing can use the compatibility block directly. For example:

action=touchInput:a1,a2:v1,v1:+2,+5

can register each actor by its actorName value while preserving the executing target as the binding owner. Removing either the actor target or the owning target removes the corresponding touch binding.

Pose routing follows the same pattern:

action=poseInput:jump:v1:+2
action=poseInput:jump:
action=poseInput:

These map to registering jump for the current target, removing that target's jump binding, and removing every pose binding owned by the current target.

Blocks

listen for key [KEY_ID] set runtime var [RUNTIME_VAR] to [VALUE]

Registers or replaces a target-owned key binding.

| Property | Value | |---|---| | Type | Command | | Opcode | listenForKey | | Feature flag | asyncInput | | KEY_ID | String, default: KeyA | | RUNTIME_VAR | String, default: input | | VALUE | String, default: pressed |

listen for key [KEY_ID] set runtime var [RUNTIME_VAR] to [VALUE] and broadcast [MESSAGE]

Registers or replaces a target-owned key binding that broadcasts after updating the runtime variable.

| Property | Value | |---|---| | Type | Command | | Opcode | listenForKeyAndBroadcast | | Feature flag | asyncInput | | KEY_ID | String, default: KeyA | | RUNTIME_VAR | String, default: input | | VALUE | String, default: pressed | | MESSAGE | String, default: message1 |

stop listening for key [KEY_ID] for this target

Removes this target's binding for one physical key code.

| Property | Value | |---|---| | Type | Command | | Opcode | stopListeningForKey | | Feature flag | asyncInput | | KEY_ID | String, default: KeyA |

stop all key listeners registered by this target

Removes every key binding owned by the current target.

| Property | Value | |---|---| | Type | Command | | Opcode | stopAllKeyListeners | | Feature flag | asyncInput |

listen for touch on this sprite set runtime var [RUNTIME_VAR] to [VALUE]

Registers or replaces the current sprite or clone's pointer binding.

| Property | Value | |---|---| | Type | Command | | Opcode | listenForTouch | | Feature flag | asyncInput | | RUNTIME_VAR | String, default: input | | VALUE | String, default: pressed |

listen for touch on this sprite set runtime var [RUNTIME_VAR] to [VALUE] and broadcast [MESSAGE]

Registers or replaces the current sprite or clone's pointer binding that broadcasts after updating the runtime variable.

| Property | Value | |---|---| | Type | Command | | Opcode | listenForTouchAndBroadcast | | Feature flag | asyncInput | | RUNTIME_VAR | String, default: input | | VALUE | String, default: pressed | | MESSAGE | String, default: message1 |

stop listening for touch on this sprite

Removes the current target's pointer binding.

| Property | Value | |---|---| | Type | Command | | Opcode | stopListeningForTouch | | Feature flag | asyncInput |

listen for accumulated pose [POSE_NAME] set runtime var [RUNTIME_VAR] to [VALUE]

Registers or replaces a target-owned accumulated pose binding.

| Property | Value | |---|---| | Type | Command | | Opcode | listenForPose | | Feature flag | poseInput | | POSE_NAME | String, default: jump | | RUNTIME_VAR | String, default: input | | VALUE | String, default: detected |

stop listening for accumulated pose [POSE_NAME] for this target

Removes this target's binding for one accumulated pose name.

| Property | Value | |---|---| | Type | Command | | Opcode | stopListeningForPose | | Feature flag | poseInput | | POSE_NAME | String, default: jump |

stop all pose listeners registered by this target

Removes every accumulated pose binding owned by the current target.

| Property | Value | |---|---| | Type | Command | | Opcode | stopAllPoseListeners | | Feature flag | poseInput |

stop all input listeners registered by this target

Removes every key, pointer, and accumulated pose binding owned by the current target.

| Property | Value | |---|---| | Type | Command | | Opcode | stopAllInputListeners | | Feature flag | asyncInput |

Development

npm install --global corepack@latest
corepack enable
pnpm install --frozen-lockfile
pnpm run check

Release

Release commits keep package.json, the pinned npm/CDN examples in this README and the two GitHub Pages guides, and CHANGELOG.md on the same version. After the release PR is merged and CI and the Pages deployment succeed, publish the exact package version, create the matching v<version> tag on the merge commit, and create the GitHub Release from that tag. Consumers can roll back by pinning the preceding npm version.

The build produces dist/async-input.js, dist/composition.js, and dist/types/composition.d.ts. Commit rebuilt artifacts whenever extension or Composition source changes.

License

MPL-2.0