A TypeScript toolchain for .milk presets: a lossless parser, an EEL2 interpreter and JIT, an intermediate representation, HLSL analysis of the warp and composite shaders with GLSL emission, EEL-to-WGSL compute lowering, a formatter, exact MilkDrop 2 export, and dataflow analysis that reads which audio reaches which control. It keeps presets as .milk files: exportMilkdrop2Preset writes the file format MilkDrop 2 saves, which MilkDrop and projectM open directly and Butterchurn reads only after conversion to its JSON preset format. Born in Stims, where the same code compiles every preset the visualizer plays.
Preset text goes through a lossless line tree, a field parser and the IR builder, which also parses every equation and classifies the shader text. From the IR, a program block runs on the tree-walking interpreter or the JavaScript JIT, or is lowered to WGSL by the generator; all three read one function table. The formatter and exporter turn the IR back into text.
src/. Gold outlines are outputs; the IR is the one shared reading of what a preset means.The library, bundled from the same dist/ the package ships, running in your tab. Edit the preset; everything below recompiles as you type.
compiled.diagnosticsWaiting for the first compile.
compiled.irWaiting for the first compile.
analyzePresetDataflowWaiting for the first compile.
compileProgramToWgslWaiting for the first compile.
Waiting for the first compile.
parseMilkdropExpression + evaluateMilkdropExpression…
Every other name reads as 0. 3 / 0 contributes 0, not Infinity; that is EEL2, not a bug.
Everything is exported from the package root; every module under src/ is also importable as milkdrop-toolchain/src/<file>.ts. The main entries, grouped as in the README.
| Export | Purpose |
|---|---|
parsePresetSyntax(source) / printPresetSyntax(tree) | Lossless line-level syntax tree of a .milk file; printing gives back the exact bytes. |
fieldAssignments(tree), isShaderSection(name), splitInlineComment(line) | Helpers over the syntax tree. |
parseMilkdropPreset(source) | Fields and sections of a preset, with parse diagnostics. |
| Export | Purpose |
|---|---|
compileMilkdropPresetSource(raw, source?, options?) | Parse, build the IR, format, and cache; returns a MilkdropCompiledPreset. |
createIR(ast, diagnostics, source?, options?), createMilkdropIr(...) | The IR builder without the cache or formatter. |
buildParityReport(ir, options) | Per-backend parity report: what runs as written, what is approximated, what is blocked. |
classifyFidelity, buildBackendDivergence, buildVisualFallbacks, buildDegradationReasons | The pieces of the compatibility classification. |
DEFAULT_MILKDROP_STATE, createDefaultCustomWaveSlot(), createDefaultShapeSlot() | MilkDrop's default per-frame state and slot templates. |
clearCompiledPresetCache(), warmupCompiledPresetCache(presets) | Control the compile cache. |
| Export | Purpose |
|---|---|
parseMilkdropExpression(source, line) | Parse one expression to an AST, with diagnostics. |
parseMilkdropStatement(source, line), splitMilkdropStatements(source) | Parse target = expr; statements, split a block on ;. |
evaluateMilkdropExpression(node, env, helpers?) | Tree-walking interpreter. |
compileMilkdropProgram(block) | JIT a program block to a JavaScript function; falls back to the interpreter when new Function is blocked by CSP. |
compileProgramToWgsl(block, options?), buildWgslExpressionString(node, ...) | Lower a program block, or one expression, to WGSL. |
MILKDROP_WGSL_SIGNAL_FIELDS, MILKDROP_WGSL_SIGNAL_ALIAS_MAP | The VmSignals uniform layout and its aliases. |
EEL_FUNCTIONS, EEL_BINARY_OPERATORS, EEL_UNARY_OPERATORS, getEelFunctionSpec(name) | The one function table the interpreter, JIT and WGSL generator all read. |
MILKDROP_FIELDS, buildFieldAliasMap(), MILKDROP2_FIELD_PAIRS | The field table: every known preset field, its type, default and spellings. |
MILKDROP_BUILTIN_DOCS, MILKDROP_INTRINSIC_FUNCTION_NAMES, findNearestMatch(name, candidates) | Documentation of every builtin, and spelling suggestions, for editors and linters. |
| Export | Purpose |
|---|---|
extractShaderSource(fields, stage), ensureShaderBody(text) | Recover the warp or comp shader text from the preset's backtick lines. |
parseMilkdropShaderStatement(line), evaluateMilkdropShaderExpression(node, env) | Parse one HLSL statement to a shader AST; constant-evaluate a shader expression. |
extractShaderControls(...), evaluateMilkdropShaderControlProgram(...) | Read, and evaluate per frame, the colour, texture and warp controls a shader body sets. |
normalizeHlslToGlsl(body, ...), generateGlslFromShaderStatements(statements, stage) | Translate an HLSL shader body to GLSL ES, or rebuild GLSL from parsed statements. |
describeShaderTranslations(compiled), resolveShaderExecutionMode(compiled, backend) | Per-stage summary: source, GLSL, which path produced it, and whether each backend runs it as written. |
MILKDROP_SHADER_TEXTURE_SAMPLERS, MILKDROP_TEXTURE_FILES | The sampler name tables and the texture files MilkDrop ships. |
| Export | Purpose |
|---|---|
formatMilkdropPreset(compiled) | Serialize a compiled preset back to .milk text in the editor dialect. |
exportMilkdrop2Preset(compiled) | Write the MilkDrop 2 file format: MILKDROP_PRESET_VERSION, MilkDrop's key names, backtick shader lines. |
readMilkdropField(source, key), upsertMilkdropField(source, key, value), upsertMilkdropFields(source, entries) | Read or set fields in preset text without disturbing the rest. |
isFieldShadowedByEquations(...), getFieldOverwriteKind(...) | Which fields an equation overwrites each frame. |
lineageFromFields(fields), lineageFieldLines(refs) | Remix lineage metadata stored in the preset. |
| Export | Purpose |
|---|---|
analyzePresetDataflow(ir) | Which audio signals reach which control or drawn part, read from the equations. |
controlAudio, drawnPartAudio, frameValueName, dataflowSignature | Query and fingerprint a dataflow result. |
analyzePresetMath(source) | Motion, reactivity and complexity summary of a preset's equations. |
The MilkdropCompiledPreset, MilkdropPresetIR, MilkdropProgramBlock, MilkdropExpressionNode, MilkdropDiagnostic, MilkdropShaderStatement and MilkdropRuntimeSignals types are exported from the root.
The package stops where the GPU starts. Four things a complete engine needs that live in the host instead.
Drawing waves, shapes, the warp mesh and the composite pass, and feeding audio into the signal environment, is the host's job. Stims does it with three.js on WebGL and WebGPU.
compileMilkdropProgram gives you a function per program block; the loop that runs init, per-frame, per-pixel and the custom wave and shape programs in order, with the right register bank, and blends between presets, lives in Stims (vm.ts).
Dispatching the WGSL this package generates and reading the state buffer back.
In Stims the shader text becomes WGSL through three.js TSL nodes, which this package does not depend on. The HLSL side here stops at the GLSL translation and the control analysis.
The EEL2 semantics implemented by the interpreter, the JIT and the WGSL generator follow the corpus in the sibling package eel-conformance (npm): portable JSON cases, a JSON Schema and a runner contract. This package does not depend on the corpus or run it. Stims runs it against three CPU paths, single-statement evaluation, the CSP-fallback interpreter and the JIT, in tests/unit/eel-conformance-spec.test.ts; no WGSL tier runs it. Five behaviours that differ from JavaScript and trip up most reimplementations:
0, not Infinity or NaN (3 / 0 is 0).% truncates both operands to integers first, and a zero divisor yields 0.|v| > 0.00001: if(0.000001, a, b) takes the b branch.bor and band are logical, not bitwise: bor(0.5, 0) is 1.Zoom, ZOOM and zoom are the same variable, and pi and e are ordinary prepopulated variables a preset may overwrite.Try any of them in the scratchpad above.