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

@spacedevin/deck

v1.10.0

Published

.deck music language — tokenize, parse, format, registries and highlight classification. One Tish source, three targets (Tish, JS, Rust)

Readme

deck

A tiny text language for writing music, and the packages that play it.

npm: @spacedevin/deck npm: @spacedevin/deck-synths npm: @spacedevin/deck-player crates.io: deckfile CI License: MIT

Hear it on the docs site · Examples · Grammar · Contributing

deck 1
bpm 132

track Lead id lead gen gameBoyDmg
  gen type pulse duty 25 vol 11
  note 72 0 0.5 v 110
  note 76 0.5 0.5 v 95
  note 79 1 1 v 105
  note 76 2 0.5 v 100
  note 72 2.5 1.5 v 110

track Bass id bass gen gameBoyDmg
  gen type wave wave_shape saw vol 15
  note 36 0 2 v 120
  note 43 2 2 v 110

track Kick id kick gen gbaDirectSound
  gen waveform triangle pitch_drop -14
  adsr a 0 d 0.08 s 0 r 0
  step_pitch 36
  steps x . . . x . . x x . . . x . . .

That is a whole song: a tempo, three tracks, and what each one plays — melody as notes on a beat grid, drums as a step pattern. Press play on the docs site and the browser synthesises it while the code lights up: the step under the playhead in each lane, and the lines of every track sounding on it.

.deck is line-oriented and streamable, so it can be typed, diffed, generated, and sent over a wire a line at a time. Times are in quarter-note beats; one bar is 4 beats or 16 sixteenth steps. The parser is deliberately parse-only — absent optionals stay null and nothing is clamped — because defaults and ranges are the host's policy.

The packages

| Package | What it is | Install | |---|---|---| | @spacedevin/deck | The language: tokenize, parse, format, registries, highlight classification. No audio. | npm i @spacedevin/deck | | @spacedevin/deck-synths | The instrument catalog: 33 Web Audio voices — Game Boy, NES, C64 SID, YM2612, SPC700, FM, drums, hard sync, bowed and plucked models, vocals. | npm i @spacedevin/deck-synths | | @spacedevin/deck-player | The host: Song IR with defaults and clamps, a lookahead transport, offline render, and a <deck-player> element. | npm i @spacedevin/deck-player |

Dependencies point one way — player → synths → deck — so the language stays audio-free and the voices can be reused by any host. The same Tish source also emits a Rust crate, deckfile, checked against the same conformance corpus as the JS build.

Quick start

Play it in a page. No framework, no build step:

<script type="module" src="/node_modules/@spacedevin/deck-player/element/deck-player-element.js"></script>

<deck-player>
deck 1
bpm 120
track Lead id lead gen gameBoyDmg
  note 60 0 0.5 v 100
</deck-player>

<deck-player src="/music/theme.deck"></deck-player>

Play it from code:

import { createDeckPlayer } from '@spacedevin/deck-player'

let player = createDeckPlayer()
let song = player.load(source)          // returns the Song, with errors / substitutions / ignored
button.onclick = () => player.play()    // an AudioContext needs a user gesture

Parse it only:

import { parseProgram, registerGeneratorIdAliases, registerGenBlockDialect } from "@spacedevin/deck"

registerGeneratorIdAliases({ matrix_fm: "matrixFm" }, { matrixFm: "matrix_fm" })
// registerGenBlockDialect(...) — host supplies patch / matrix_fm parsers

let ast = parseProgram(source)

Render it to a WAV. From a checkout of this repo, with Chrome or Chromium installed:

node scripts/render-wav.mjs song.deck -o song.wav

The voices are Web Audio, so the renderer drives a headless Chrome and an OfflineAudioContext. It is deterministic and faster than real time. Details and flags in Rendering.

Docs

spacedevin.github.io/deck — the same markdown, as a site, with a play button on every complete song.

For LLM readers there is an llms.txt and a single-file llms-full.txt, generated from the same pages. npm also exports ./grammar, ./ast, ./examples, ./rendering, ./extension and ./host to those markdown files.

What the language package covers

| Area | API | |------|-----| | Lex / parse | tokenize, isNumberToken, parseProgram | | Track / clip body | parseBodyLine, parseTrackBody, parseBoolish | | Format | formatTplBeat, formatTplFloat | | Scale | parseScaleRoot, scaleRootNames, scaleModeNames, scaleIntervals | | Bar / Euclid | parseBarSelector, barSelectorMatches, euclideanPattern | | Registries | registerGeneratorIdAliases, registerParamKeyAliases, paramKeyToCamel, … | | Host extensions | registerBodyLineDialect, registerTopLevelStatement, registerGenBlockDialect | | Macros | registerBuiltinMacros, lookupMacro, expandMacroBody | | gen_block | parseGenBlock, registerGenBlockDialect | | Highlight | classifyLine, isKeyword, registerHighlightKeywords |

Out of scope for the language package, and owned by hosts: apply/emit to a project IR, sessions, audio engines, instrument catalogs, builtin macro catalogs, highlight CSS, graph editors.

Runnable demos of the parse and host-boot API live in examples/:

npm run examples

Rust

The same src/index.tish emits a Rust library crate, so a Rust consumer (tish-gba's build-time bake) parses .deck with this parser rather than its own:

npm run build:rust   # -> crate/  (crates.io: `deckfile`)
npm run test:rust    # the same conformance corpus, from Rust
let program = deckfile::parse(src);          // typed
let ast = deckfile::parseProgram(value);     // the raw AST, same shape as JS

One source, three targets — Tish, JS, Rust — checked against one corpus.

Contributing

Contributions are welcome, and small ones are a fine place to start. Good first contributions:

  • A new example in docs/EXAMPLES.md — every block there is tested and playable
  • A new voice in packages/synths/ — one pure function, one registry entry, one example
  • A conformance case in conformance/ when you find an input the parsers disagree on
  • A doc fix — the site is built from the markdown in this repo, so a PR is the whole change

CONTRIBUTING.md has the setup, the test commands, the commit conventions, and a recipe for each of those. Bugs and ideas go in issues; there are templates for a bug, a feature, and a new voice.

Development

npm install
npm test                 # build + API/grammar suite + conformance + examples + tish and JS smoke
npm run test:coverage    # c8 on dist/deck.js — 100% lines / functions / statements
npm run test:conformance # the cross-implementation corpus
npm test -w @spacedevin/deck-player
npm run site:serve       # the docs site on :4321

conformance/ is the contract between implementations: the same .deck inputs and expected parses are run by the JS build, the Rust crate emitted from the same Tish source, and any restricted host (via a profile). It is what makes drift a test failure rather than a surprise.

Branch coverage is lower (~60%) because the Tish→JS emit adds many ?? null / typeof guards that are defensive noise, not language logic. Line coverage is the gate in CI.

Releases

Versions come from sem: Conventional Commits drive semver (feat / fix / perf / BREAKING release; chore / docs / ci do not). A green main cuts a prerelease carrying all three npm tarballs; promoting it to a full release publishes to npm and crates.io. The Releases page is the changelog.

License

MIT — see LICENSE.