An interactive platform to introduce SuperCollider through hands-on exercises in the browser. No installation required.
SuperCollider Learn is not meant to be a full SuperCollider environment or a complete reference of the language. The goal is to lower the barrier to entry — give beginners a feel for SuperCollider's syntax and audio concepts before they commit to installing and learning the real software.
A few things this project deliberately does not do:
- The actual SuperCollider server (
scsynth) does not run here. Audio is simulated via Tone.js, which approximates the sound of common UGens in the browser. - Not all UGens are covered. Only a curated subset relevant to the exercises is documented. The UGen reference and glossary are teaching aids, not a full language spec.
- Code is not real SC code. The editor validates SuperCollider-style syntax and simulates the audio output, but it does not execute actual SuperCollider code.
If you want to contribute content, keep this scope in mind: the aim is a clear, progressive introduction — not completeness.
- Progressive exercises — leveled exercises that introduce UGens (Unit Generators) step by step
- Live audio — code is evaluated and played back in real time via Tone.js
- Code editor — syntax-highlighted editor with SuperCollider coloring
- UGen reference — built-in glossary and UGen catalog
- Progress tracking — exercise completion is saved to
localStorage, no account needed - Multilingual — UI available in English, Spanish, and French
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, TypeScript 5 |
| Styling | CSS Modules, CSS custom properties |
| Code editor | Custom <textarea> with syntax highlighting |
| Audio engine | Tone.js 15 |
| i18n | Built-in EN / ES / FR translations |
src/
├── app/ # Next.js App Router pages
│ ├── about/ # About page
│ ├── exercises/ # Exercise player
│ ├── glossary/ # Glossary page
│ ├── progress/ # Progress overview
│ └── ugens/ # UGen reference
├── components/ # Shared UI components
├── context/ # React contexts (Theme, Progress, Exercises)
├── data/ # Static content (exercises, UGens, glossary, themes)
├── hooks/ # Custom hooks (useAudio)
├── i18n/ # Translations (EN / ES / FR)
├── lib/ # Utilities (audio engine, parser, highlight, progress)
└── types/ # TypeScript types
Content lives in src/data/. Adding a new exercise, UGen entry, or glossary term only requires editing those files — no logic changes needed.
The site supports English (default), Spanish, and French. You do not need to speak all three languages to contribute — see the convention below.
There are two places where text lives:
1. UI strings — labels, buttons, aria attributes, error messages. All live in src/i18n/ui.ts under three locale keys:
const ui = {
en: { exercises_run: "▶ Run" },
es: { exercises_run: "▶ Ejecutar" },
fr: { exercises_run: "▶ Exécuter" },
}Components read the current locale via useLang() and call t(lang, "key") to get the right string.
2. Content strings — exercise titles, theory text, glossary definitions, UGen descriptions. These are stored inline in src/data/ as LocalizedString objects:
title: {
en: "Basic sine wave",
es: "Onda sinusoidal básica",
fr: "Onde sinusoïdale de base",
}You only need to translate what you know. For locales you don't speak, write "NEEDS_TRANSLATION" as a placeholder:
// In src/i18n/ui.ts — adding a new UI key:
en: { my_new_label: "My label" },
es: { my_new_label: "NEEDS_TRANSLATION" },
fr: { my_new_label: "NEEDS_TRANSLATION" },
// In src/data/exercises.ts — adding a new exercise:
title: {
en: "Ring modulation",
es: "NEEDS_TRANSLATION",
fr: "NEEDS_TRANSLATION",
},The build will still pass. Another contributor can follow up with a focused PR that only fills in the missing strings.
Translation-only PRs are very welcome — if you find a NEEDS_TRANSLATION and know the language, open a PR that fills it in. No other code changes needed.
The type system ensures every locale key exists. If you add a key to en but forget es or fr, tsc will fail. The NEEDS_TRANSLATION string is the safe way to satisfy the type checker while marking the work as incomplete.
No real SuperCollider runs in the browser. The audio pipeline is a custom approximation built on Tone.js.
User writes SC code
│
▼
scCodeParser.ts ──── validateSCCode() → structural checks (braces, .play, UGen presence)
└─── parseSCCode() → AudioConfig object
│
▼
AudioConfig (intermediate data format — freq, amp, type, lfo, env, filter, reverb…)
│
▼
useAudio.ts ── play(AudioConfig) → selects the right factory function
│
▼
audio.ts (factory functions) → creates and connects Tone.js nodes
│
▼
Tone.js → browser audio output
| File | Role |
|---|---|
src/lib/scCodeParser.ts |
Parses SC syntax into AudioConfig via regex. Also validates structure. |
src/types/index.ts |
Defines AudioConfig — the data contract between parser and engine. |
src/hooks/useAudio.ts |
React hook. Manages Tone.js lifecycle: starts context, routes AudioConfig to the right factory, disposes nodes on stop. |
src/lib/audio.ts |
Factory functions (createSynth, createNoise, createLFOSynth, createEnvSynth, createSweepSynth, createFilteredSynth, createReverbSynth, createDelay…). Each one wires Tone.js nodes together. |
The parser covers a deliberate subset of SuperCollider UGens:
- Oscillators —
SinOsc,Saw,Pulse,LFTri - Noise —
WhiteNoise,PinkNoise,BrownNoise - LFO modulation —
LFSaw,LFPulse,LFTriused as modulators (FM and AM) - Envelopes —
Env.perc - Frequency sweeps —
XLine,Line - Spatial —
Pan2(stereo panning)
Anything outside this list will not produce audio. Complex signal graphs, multiple sound sources, and routing chains are not supported.
There are two cases depending on where the sound is needed:
UGen reference page — the sound field on each entry in src/data/ugens.ts is a hardcoded AudioConfig object. The parser is not involved. Just set the fields directly:
sound: { type: "sine", freq: 440, amp: 0.3 }Exercise editor — the user writes code and clicks Run. The parser must be able to extract the right parameters from the text. This requires:
- Adding a regex-based detection function in
scCodeParser.tsthat reads the relevant arguments and returns the rightAudioConfigfields - If the sound type is new, adding a factory function in
audio.tsand a matching branch inuseAudio.ts
# Install dependencies
npm install
# Start the development server
npm run dev
# Build for production
npm run build
# Run linter
npm run lintAdd an entry to src/data/exercises.ts. Text fields (title, goal, theory, starter, answer) are LocalizedString — provide all three locales or use "NEEDS_TRANSLATION" for the ones you don't know. Each exercise also implements a validate(code: string) function that returns { ok, tips } where tips is a LocalizedString[]. Look at existing exercises for reference.
Add an entry to src/data/ugens.ts. The description, note[], and each argument's desc are LocalizedString.
Add an entry to src/data/glossary.ts. Both term and definition are LocalizedString.
Contributions are welcome. The project follows a standard open-source workflow:
- Open an issue first for any non-trivial change (new feature, significant refactor).
- Fork the repository and create a branch from
main.git checkout -b feat/your-feature-name - Make your changes. Keep commits focused — one logical change per commit.
- Follow Conventional Commits for commit messages:
feat: add LFO exercise to level 3 fix: correct validate function for WhiteNoise docs: update UGen reference for Reverb - Open a pull request against
main. Describe what the change does and why.
- New exercises (new UGens, new audio concepts)
- Improvements to existing exercise feedback/tips
- UGen reference entries
- Glossary terms
- Filling in
NEEDS_TRANSLATIONplaceholders for any language you speak - Accessibility improvements
- UI bugs
MIT