Dogalog is a realtime Prolog-based livecoding music environment where you write logic rules to create algorithmic rhythmic patterns and melodies.
npm install
npm run dev
Open the dev server URL, click Tutorial to learn, or click Start and begin livecoding!
Rules are Prolog-like statements that define when sounds should play:
% Kick drum on every beat
event(kick, 60, 80, T) :- beat(T, 1).
% Snare on beats 2 and 4
event(snare, 60, 90, T) :- beat(T, 2).
% Hi-hats every 8th note
event(hat, 60, 70, T) :- every(T, 0.5).
The system asks event(Voice, Pitch, Velocity, Time) on every step and plays all matching results.
beat(T, N) - Trigger on beat N (1 = downbeat, 2 = halfway, 4 = quarter notes)every(T, Step) - Trigger at regular intervals (0.5 = twice per beat)phase(T, N, K) - Trigger on phase K of N divisionseuc(T, K, N, B, R) - Euclidean rhythm (K hits over N steps)prob(P) - Succeed with probability P (0.0-1.0)choose(List, X) - Pick random elementcycle(List, X) - Cycle through elements sequentially (stateful!)pick(List, X) - Backtrack through all elementsrand(Min, Max, X) - Random floatrandint(Min, Max, X) - Random integerscale(Root, Mode, Degree, Oct, Midi) - Convert scale degree to MIDI notechord(Root, Quality, Oct, Midi) - Generate chord tonestranspose(Note, Offset, Out) - Transpose by semitoneswithin(T, Start, End) - Time range check (for song structure)cooldown(Now, Last, Gap) - Prevent rapid retriggeringeq(A, B), lt(A, B), gt(A, B) - Comparisonsdistinct(List) - Check all elements are uniqueadd(A, B, C) - Arithmeticrange(Start, End, Step, X) - Generate number sequencesrotate(List, Shift, OutList) - Rotate listskick - Synthesized kick drumsnare - Noise-based snarehat - Noise-based hi-hatsine - Sine wave monosynth (use MIDI pitch)Four-on-the-floor with backbeat:
event(kick, 60, 100, T) :- euc(T, 4, 16, 4, 0).
event(snare, 60, 90, T) :- euc(T, 2, 16, 4, 4).
Complex polyrhythm:
event(kick, 60, 100, T) :- euc(T, 3, 8, 0.5, 0).
event(snare, 60, 80, T) :- euc(T, 5, 8, 0.5, 2).
Pentatonic melody:
note(N) :- cycle([1, 3, 4, 5, 8], D), scale(60, minor_pent, D, 0, N).
event(sine, N, 70, T) :- every(T, 0.5), note(N).
Available modes: major, minor, dorian, phrygian, lydian, mixolydian, locrian, minor_pent, major_pent, blues, whole_tone, chromatic
arp(N) :- chord(60, minor, 0, Notes), choose(Notes, N).
event(sine, N, 70, T) :- every(T, 0.25), arp(N).
Chord qualities: maj, min, dim, aug, sus2, sus4, maj7, min7, dom7, dim7
Random velocities:
vel(V) :- choose([60, 80, 100], V).
event(hat, 60, V, T) :- every(T, 0.25), vel(V).
Sparse pattern:
event(snare, 60, 80, T) :- beat(T, 2), prob(0.3).
within% Intro: just kick (beats 0-8)
event(kick, 60, 100, T) :- beat(T, 1).
% Verse: add snare (beats 8-16)
event(snare, 60, 80, T) :- beat(T, 2), within(T, 8, 16).
% Chorus: add melody (beats 16-24)
melody(N) :- scale(60, major, D, 0, N), cycle([1,3,5,8], D).
event(sine, N, 70, T) :- every(T, 0.5), melody(N), within(T, 16, 24).
cooldown% Regular pattern
event(kick, 60, 100, T) :- beat(T, 1).
% Fill every 4+ bars
fill(T) :- beat(T, 1), cooldown(T, last_fill, 4).
event(snare, 60, V, T) :- fill(T), every(T, 0.25), choose([80,90,100], V).
npm install # Install dependencies
npm run dev # Start dev server (http://localhost:5173)
npm run build # Build for production
npm run preview # Preview production build
npm test # Run tests
npm run test:ui # Run tests with UI
npm run test:coverage # Generate coverage report
npm run docs:html # Build manual and cheatsheet
src/
├── prolog/ # Prolog engine
│ ├── builtins/ # Builtin predicates (modular)
│ ├── parser.js # Parser
│ ├── resolution.js # SLD resolution with generators
│ ├── tokenizer.js # Tokenizer
│ ├── unify.js # Unification
│ └── terms.js # Term constructors
├── audio/ # WebAudio synthesis
│ └── audioEngine.js
├── scheduler/ # Timing and execution
│ ├── scheduler.js
│ ├── stateManager.js
│ └── transitionManager.js
├── livecoding/ # Auto-evaluation
│ ├── codeValidator.js
│ └── liveEvaluator.js
├── tutorial/ # Tutorial system
│ ├── tutorialManager.js
│ ├── tutorialOverlay.js
│ └── steps.js
├── ui/ # User interface
│ ├── components/ # Reusable components
│ ├── template.js
│ ├── controls.js
│ └── validationIndicator.js
├── help/ # Documentation
│ └── builtinDocs.js
├── config/ # Configuration
│ └── defaults.js
├── app.js # Application orchestrator
└── main.js # Entry point
% This pattern's state persists when you edit other code:
drums(D) :- cycle([kick, snare, hat], D).
event(D, 60, 80, T) :- beat(T, 1), drums(D).
% Editing this won't reset the cycle counter!
Euclidean rhythms distribute K hits as evenly as possible over N steps using the Euclidean algorithm. The result is musically interesting patterns used in music worldwide:
euc(T, 3, 8, 0.5, 0) - Tresillo pattern (Cuban music)euc(T, 5, 8, 0.5, 0) - Cinquillo patterneuc(T, 5, 12, 0.5, 0) - Common rock beatMIT
Built with vanilla JavaScript, CodeMirror 6, and WebAudio API. Inspired by TidalCycles, Sonic Pi, and Datalog.
$ claude mcp add dogalog \
-- python -m otcore.mcp_server <graph>