Score is an open-source EDM audio framework. Contributions are welcome — instruments, synthesis techniques, DSL methods, effects, tests, and documentation.
Read this before opening a PR.
Score has a strict design philosophy. Every contribution must follow it.
- Math reads as music. Frequencies, rhythms, envelopes — a song is a pure function of time.
- Factory functions, not classes.
createKick808(props)returns a plain object. Nevernew. - Zero
let, zeroclass, zeronew(except Web Audio API internals). - Immutable config. Never mutate props — return new state.
constand arrow functions only.ScoreErrorfactory for all errors. Neverthrow new Error(...).- No AI-generated music, patterns, or full songs. Ever.
- No audio samples bundled. Users bring their own.
- Song files run as plain ESM. Never compiled.
If you haven't read the ADRs in docs/adr/, start there. ADR 001 and ADR 014 are essential.
# Prerequisites: Node 20 LTS, pnpm
git clone https://github.com/bwyard/score
cd score
pnpm install
pnpm turbo build --filter='!@score/gui'
pnpm testBranch off dev. PRs go into dev. Never target main directly.
git checkout dev
git checkout -b feat/your-featureNew synthesis engines following the AudioComponent interface. See existing engines in packages/components/src/ for examples:
createKick808.ts— envelope + sine bodycreateFMSynth.ts— operator-based FMcreateSubtractiveSynth.ts— oscillator + filter + envelope
Every instrument needs:
- Factory function returning a plain object (
AudioComponentshape) - Full TSDoc on all public exports
- Tests shipping with the component (not backfilled)
- Chain method support via
ChainableTrackfrom@score/dsl
New .method() additions to the fluent DSL (ADR 014). Pitch, modulation, effects, sequencing. Open an issue first to discuss scope.
@score/effects — reverb, delay, distortion, etc. Must be functional, no class instances.
@score/pattern and @score/dsl — pattern utilities, mini-notation, theory helpers.
Always welcome. Include a failing test that the fix makes pass.
- TypeScript strict mode. No
any. - All public exports get TSDoc:
/** */block with@param,@returns,@example,@throws {ScoreError}. - Tests use Vitest. Coverage thresholds: 90/85/90/90 (statements/branches/functions/lines).
- CI must pass before merge:
typecheck → lint → test → coverage. - Conventional commits:
feat:,fix:,docs:,test:,chore:. - IO and hardware boundaries annotated with
// BOUNDARY — [reason].
- Branched off
dev, targetingdev - All tests pass (
pnpm test) - No
let, noclass, nonew(outside Web Audio API) - Every public export has TSDoc
- Tests ship with the component (not in a separate PR)
-
ScoreErrorused for all errors - CI green
- AI-generated music, patterns, or songs
- Use of contributions as AI training data
- Bundled audio samples
- Class-based architecture
- Direct mutations of config objects
- Raw
throw new Error(...)— useScoreError - PRs that break existing tests without explanation
- Features that conflict with ADRs without a new ADR discussion
Open an issue on GitHub. For security vulnerabilities, email byard29@gmail.com directly — do not open a public issue.
By contributing, you agree that your contributions will be licensed under the Apache License 2.0. See LICENSE.