
blibliki-engine
by mikezaby
Schema-driven audio engine for Web and Node
SKILL.md
name: blibliki-engine description: Use when working with Blibliki's packages/engine - creating modules, modifying engine code, debugging MIDI/audio issues, or when changes in engine package don't appear in grid app
Blibliki Engine
Overview
The Blibliki engine (packages/engine) is a TypeScript audio synthesis framework built on Web Audio API. Critical: Apps consume BUILT packages from dist/, not source files. Changes require rebuilding before they appear in apps.
Monorepo Build Workflow
The #1 mistake: Modifying engine source and expecting apps to pick it up without rebuilding.
digraph build_workflow {
"Modifying engine code?" [shape=diamond];
"Edit packages/engine/src/" [shape=box];
"Run: pnpm build:packages" [shape=box, style=filled, fillcolor=yellow];
"Edit apps/grid/src/" [shape=box];
"Run: pnpm dev to test" [shape=box];
"Just reading/understanding?" [shape=diamond];
"Read code directly" [shape=box];
"Modifying engine code?" -> "Edit packages/engine/src/" [label="yes"];
"Edit packages/engine/src/" -> "Run: pnpm build:packages";
"Run: pnpm build:packages" -> "Edit apps/grid/src/" [label="if updating UI"];
"Edit apps/grid/src/" -> "Run: pnpm dev to test";
"Modifying engine code?" -> "Just reading/understanding?" [label="no"];
"Just reading/understanding?" -> "Read code directly" [label="yes"];
}
Why this matters:
- Apps import from
packages/engine/dist/(built artifacts), notsrc/ pnpm testvalidates source but doesn't build- Without rebuild, apps use stale code
Commands:
pnpm dev- Start dev servers with watch mode (RECOMMENDED - auto-rebuilds on save)pnpm build:packages- Rebuild all packages (use when working withoutpnpm dev)pnpm test- Run tests (validates source, doesn't build)pnpm tsc- Type checking
When to rebuild:
- Using
pnpm dev: No manual rebuild needed - changes auto-rebuild on save. Check terminal for build errors. - Not using
pnpm dev: Must runpnpm build:packagesmanually after engine changes before testing in apps.
Module Implementation Patterns
Constructor Signature
CRITICAL: Module constructor signature is fixed.
// ✅ CORRECT - Mono module
class MonoGain extends Module<ModuleType.Gain> {
constructor(engineId: string, params: ICreateModule<ModuleType.Gain>) {
const props = { ...DEFAULT_PROPS, ...params.props };
const audioNodeConstructor = (context: Context) =>
new GainNode(context.audioContext);
super(engineId, { ...params, audioNodeConstructor, props });
this.registerDefaultIOs();
}
}
// ❌ WRONG - Different signature
constructor(engine: Engine, id: string, props?: any)
Registering IOs
Use register* helper methods, NOT direct AudioIO construction.
// ✅ CORRECT - AudioParam modulation input
this.registerAudioInput({
name: "gain",
getAudioNode: () => this.audioNode.gain, // Returns AudioParam
});
// ✅ CORRECT - MIDI input
this.registerMidiInput({
name: "midi in",
onMidiEvent: this.onMidiEvent,
});
// ❌ WRONG - Don't construct AudioIO directly
this.inputs = {
gain: new AudioIO(this, 'gain', 'input', this.audioNode.gain)
};
Quick reference:
registerAudioInput(props)- Audio/AudioParam inputsregisterAudioOutput(props)- Audio outputsregisterMidiInput(props)- MIDI inputs with event handlerregisterMidiOutput(props)- MIDI outputsregisterDefaultIOs()- Auto-registers standard "in"/"out" + "midi in"
Module Props Schema
CRITICAL: Schema is a TypeScript type, not a function. Use kind not type.
// ✅ CORRECT
export type IGainProps = { gain: number };
export const gainPropSchema: ModulePropSchema<IGainProps> = {
gain: {
kind: "number", // kind, not type
min: 0,
max: 2,
step: 0.01,
label: "Gain",
},
};
// ❌ WRONG
modulePropSchema({ gain: { type: 'number', ... }})
Applying Props with Setter Hooks
Props don't automatically update Web Audio nodes. Use setter hooks.
class MonoGain extends Module<ModuleType.Gain>
implements Pick<SetterHooks<IGainProps>, "onAfterSetGain">
{
// ✅ CORRECT - Hook called when props.gain changes
onAfterSetGain: SetterHooks<IGainProps>["onAfterSetGain"] = (value) => {
this.audioNode.gain.value = value;
};
}
// Available hooks per prop:
// - onSetPropName(value) - Transform before setting (return new value)
// - onAfterSetPropName(value) - Side effect after setting (void)
PolyModule Pattern
CRITICAL: PolyModule takes monoModuleConstructor, not pre-allocated voices.
// ✅ CORRECT - Poly module
export default class Gain extends PolyModule<ModuleType.Gain> {
constructor(engineId: string, params: IPolyModuleConstructor<ModuleType.Gain>) {
const props = { ...DEFAULT_PROPS, ...params.props };
// Pass constructor function for mono voices
const monoModuleConstructor = (
engineId: string,
params: IModuleConstructor<ModuleType.Gain>
) => new MonoGain(engineId, params);
super(engineId, { ...params, props, monoModuleConstructor });
this.registerDefaultIOs();
}
}
// ❌ WRONG - Don't pre-allocate voice instances
this.voices = Array.from({ length: 8 }, () => new Voice());
Module Registration Checklist
When creating a new module, update FIVE locations:
- Create module class:
packages/engine/src/modules/YourModule.ts - Export schema:
packages/engine/src/modules/index.ts - Add to ModuleType enum:
packages/engine/src/types.ts - Create UI component:
apps/grid/src/components/AudioModule/YourModule.tsx - Register in UI mapping:
apps/grid/src/components/AudioModule/index.tsx
After registration: Run pnpm build:packages before testing in grid app.
Common Workflows
Debugging "Changes Don't Appear"
If engine changes don't appear in grid app:
- Did you run
pnpm build:packages? ← Most common cause - Check
packages/engine/dist/has updated files - Check browser console for import errors
- Restart dev server if needed
- If using
pnpm dev, it auto-rebuilds (check for build errors in terminal)
Debugging MIDI Routing
MIDI flow: MidiDevice → MidiSelector → VirtualMidi → Module.onMidiEvent
Files to check:
packages/engine/src/modules/MidiSelector.ts- Routes MIDI from devicespackages/engine/src/modules/VirtualMidi.ts- Programmatic MIDI generationapps/grid/src/lib/MidiDevice*.ts- Device management- Your module's
onMidiEventhandler and registered MidiIO inputs
Common issues:
- MidiSelector not routing to correct module
- PolyModule voices not created before IOs registered (constructor timing)
- MIDI input not registered via
registerMidiInput()
Converting to Polyphonic
- Read
packages/engine/src/core/module/PolyModule.tsfirst - Create mono version of module (handles one voice)
- Change base class from
Module<T>toPolyModule<T> - Pass
monoModuleConstructor: () => new MonoYourModule(...)to super - Voices created automatically by PolyModule base class
- Build and test with multiple simultaneous notes
Before Finishing
When completing engine work, verify ALL of these:
- Engine source changes complete
- Module schema updated if needed
- Verified packages rebuilt (check terminal if using
pnpm dev, or runpnpm build:packages) - UI components updated if needed
- Ran
pnpm test(all tests pass) - Ran
pnpm tsc(type checking passes) - Ran
pnpm lint(no linting errors) - Tested integration in grid app (with
pnpm devrunning) - Verified feature works end-to-end in browser
Most commonly missed: Verifying packages are rebuilt before testing in app.
Common Mistakes
| Mistake | Fix |
|---|---|
| "Changes don't appear in app" | Run pnpm build:packages to rebuild engine |
| "Tests pass but app breaks" | Tests validate source, apps use dist/. Rebuild packages. |
| Wrong constructor signature | Use (engineId: string, params: ICreateModule<T>) |
new AudioIO() directly | Use registerAudioInput/Output() helpers |
Schema uses type property | Use kind property instead |
modulePropSchema() function call | Plain object with type ModulePropSchema<T> |
| Props not updating Web Audio | Implement setter hooks: onAfterSetPropName |
| Pre-allocating voices in PolyModule | Pass monoModuleConstructor function |
| "Import errors in app" | Check module exported in index.ts and schema registered |
| "PolyModule voices not created" | Ensure voices created before IOs registered (constructor timing) |
File Locations
When implementing features, reference these files:
- Example modules:
packages/engine/src/modules/Gain.ts,Oscillator.ts - Module base:
packages/engine/src/core/module/Module.ts - PolyModule base:
packages/engine/src/core/module/PolyModule.ts - IO system:
packages/engine/src/core/IO/AudioIO.ts,MidiIO.ts - Schema types:
packages/engine/src/core/schema.ts
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です
