Dogalog
Dogalog is a realtime Prolog-based livecoding music environment where you write logic rules to create algorithmic rhythmic patterns and melodies.
Code Live!
Features
- Livecoding: Auto-evaluation with visual feedback - code changes apply automatically
- State Preservation: Cycle counters and cooldowns persist across updates
- Interactive Tutorial: Built-in 13-step guided tutorial for learning
- PWA Support: Install as an app, works offline
- Mobile-First: Touch-friendly UI optimized for all devices
- Modular Architecture: Clean separation of concerns, all files <100 lines
- Comprehensive Testing: 123 tests with 88%+ coverage
Quick Start
Open the dev server URL, click Tutorial to learn, or click Start and begin livecoding!
What are "rules"?
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.
Builtin Predicates
Timing
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)
Random & Variation
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 integer
Musical
scale(Root, Mode, Degree, Oct, Midi)- Convert scale degree to MIDI notechord(Root, Quality, Oct, Midi)- Generate chord tonestranspose(Note, Offset, Out)- Transpose by semitones
Logic & Control
within(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 lists
Instruments
kick- Synthesized kick drumsnare- Noise-based snarehat- Noise-based hi-hatsine- Sine wave monosynth (use MIDI pitch)
Examples
Euclidean Rhythms
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).
Melodies with Scales
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
Arpeggiated Chords
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
Probability & Variation
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).
Song Structure with 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).
Fills with 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).
Development
Build Commands
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
Project Structure
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
Architecture
- Prolog Engine: Custom implementation with SLD resolution using ES6 generators for backtracking
- Livecoding: Debounced auto-evaluation (300ms) with syntax validation
- State Management: Centralized state for cycle counters and cooldowns (persists across code updates)
- Scheduler: Time-grid based (16th notes) with swing and lookahead support
- Audio: WebAudio synthesis without samples - all sounds generated in real-time
- UI: Mobile-first, progressive enhancement, DOM-based components
Testing
- 123 tests across 16 test files
- Coverage: 88.52% statements, 90.42% functions
- Integration tests for livecoding, state preservation, example loading
- UI component tests with vitest + jsdom
PWA Features
- Installable on mobile and desktop
- Offline support with service worker
- Caches all assets and documentation
- Manifest with icons and theme colors
Documentation
- Interactive Tutorial: Click "Tutorial" button in the app for 13 guided steps
- Full Tutorial: Comprehensive guide teaching both Prolog and livecoding
- Manual: Complete reference for all built-ins and syntax
- Cheatsheet: Quick reference for common patterns
- Live Demo: GitHub Pages
Technical Details
Livecoding Flow
- User edits code in editor
- After 300ms debounce, code is validated
- If valid: parse → compile → swap program (with smooth transition)
- State (cycles, cooldowns) persists across updates
- Visual indicator shows validation state (green/red/yellow)
State Preservation
% 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
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 beat
Browser Support
- Modern browsers with WebAudio API
- Chrome/Edge 90+
- Firefox 88+
- Safari 14.1+
- Mobile browsers (iOS Safari, Chrome Android)
License
MIT
Credits
Built with vanilla JavaScript, CodeMirror 6, and WebAudio API. Inspired by TidalCycles, Sonic Pi, and Datalog.