Community-maintained reference for VirtualDJ skinning, pad pages, and VDJScript. VirtualDJ does not publish a comprehensive developer reference; this repo fills that gap.
docs/— Markdown documentation: VDJScript verb reference, skin SDK, waveform elements, pad-page schema, effects, options, filter syntax, mapper format, application internalsexamples/Pads/— focused pad page XML examples for ColorFX, samplers, cues, transport, reference patterns, and copied built-in pad pages; see examples/Pads/README.mdexamples/Skins/— skin examples, copied built-in skins, and build-system demos; GraveRaver is intentionally minimal and only demonstrates the XInclude workflowexamples/Mappers/— real working controller/keyboard mapper XML copied from a local install; ground truth for the mapper formatexamples/Samplerbanks/— sampler-bank XML copied from the app bundle (a third XML format alongside skins and pads)examples/VideoSkins/— built-in video skins (broadcast, karaoke, live) copied from the app bundle; same<skin>format as deck skins, rendered onto the video outputtests/— reproducible documentation test harnesses, pad-page XML fixtures, and the extracted data artifacts:verb-table.json(the authoritative verb set),action-contracts.json(per-verb implementation contract),verb-return-types.json(observed types and boolean truth),verb-existence-sweep.json,action-catalog.json(816 vendor descriptions),attested-tails.json(argument tails and shapes Atomix wrote),binary-vocabularies.json(argument enumerations as groups),vdjscript-corpus.json(vendor snippets from every mined source) andverb-arg-forms.json(probe results), plus theplugin-introspection*.jsoncaptures from the native channeltools/— extractors, sweeps, linters and thejustquery API; every artifact is regenerable and gated byjust check
just install # once per clone: creates .venv (uv) and installs requirements.txt
just doctor # environment health: python, packages, uv, VirtualDJ, live channel
just check # everything gated
just install needs uv (brew install uv); it
pins the interpreter from .python-version and installs requirements.txt.
Every recipe then runs .venv/bin/python3, falling back to the system python3
when no venv exists, so a fresh clone still answers a lookup question without
any setup at all.
Run just install again after a python upgrade moves the interpreter out from
under the venv. just doctor is the brew doctor of this repo and the first
thing to run when something behaves oddly: it names the interpreter in use,
whether it can import what the extraction tools need, which VirtualDJ is
installed against the builds the artifacts are anchored to, and whether the live
probe channel answers. Only the python section can fail — the extraction tools
read the VirtualDJ Mach-O binary through numpy, and the failure when it is
missing is quiet rather than loud.
| Goal | File |
|---|---|
| Answer anything about one verb | just verb <name> — the one-screen join: store record, vendor description, real usages, argument shapes with return evidence, every tail candidate by source, vocabulary groups, probe state, each labelled with its evidence tier. just get-verb <name> is the bare store record |
| Answer "how do I do X" | just topic <term> — the matching verbs, effects and XML elements, plus the real example files that use them, ranked by how much of the topic each demonstrates |
| Pick the next active maintenance task | TODO.md |
| Route a topic to the right docs and fixtures | INDEX.yml |
| Understand the repo structure and source labeling | docs/README.md |
| Pick the right VDJScript verb or pattern | docs/VirtualDJ Reference.md |
| Look up a specific verb | docs/VDJScript Verbs.md |
| Check official verb coverage | docs/Official VDJScript Coverage Audit.md |
| Choose the next completeness pass | docs/Completeness Roadmap.md |
| Choose or maintain a pad page | examples/Pads/README.md |
| Look up the pad-page XML format | docs/Pad Page XML.md |
| Build skin waveforms | docs/Skin Waveforms.md |
| Check skin/pad XML doc coverage | just list-xml-elements --undocumented (data: docs/skin-xml-inventory.json, refreshed by just inventory) |
| Look up verbs programmatically | docs/vdjscript-verb-index.json (generated; just verb-index) |
| Validate skin/mapper XML | just lint-skins [paths] · just lint-mappers [paths] · tools/README.md |
| Run or update a test harness | tests/README.md |
| Build or study a skin | docs/Skin SDK.md · docs/Skin Runtime Findings.md · examples/Skins/README.md · examples/Skins/ModularSkeleton/ |
| Work with effects | docs/Effects Usage.md · docs/Native Effects.md |
| Map a controller or keyboard | docs/Mapper XML.md |
| Write a native plugin, or understand where VDJScript results are still typed | docs/Plugin SDK.md |
| Understand macOS paths and databases | docs/Application Internals.md |
Inspect or create .vdjstems sidecars |
docs/Stem File Format.md |
AGENTS.md is intentionally retained for Claude, Codex, and other coding
agents that look for that filename. This README.md is the human-facing
overview; AGENTS.md keeps the operational shortcuts and repo-specific guardrails.
Every fact in the reference docs is labeled by source:
| Label | Meaning |
|---|---|
Official |
Current VirtualDJ manual or VDJPedia |
Official forum |
Post by VirtualDJ staff, CTO, or support |
Community |
Non-staff forum guidance |
Published skin |
Observed in a working public skin |
Built-in skin |
Observed in skin XML shipped inside the VirtualDJ app bundle |
Published pad page |
Observed in a working public pad page |
Built-in pad page |
Observed in pad-page XML shipped inside the VirtualDJ app bundle |
Built-in app resource |
Command name, description, or UI catalog entry observed in non-skin/non-pad app resources |
Verb table |
VirtualDJ's own serialised verb set, extracted from the binary — decides existence outright |
Binary compiled table |
Structured command metadata observed in compiled executable tables |
Binary symbol table |
Demangled implementation symbols observed in the VirtualDJ executable |
Binary string-table |
Command-looking string observed in the VirtualDJ executable; discovery only |
Action catalog |
The Button Editor's own description for a verb, shipped in languages.zip — the same prose the official appendix publishes, readable offline |
Vendor script |
A form Atomix wrote into a shipped Built-In pad page or skin — attested usage, no probe required |
Local test |
Reproduced in VirtualDJ locally |
Inference |
Conclusion drawn from the above sources |
Unlabeled files are raw material not yet normalized to this standard. docs/Evidence Standards.md governs every claim: three tiers, what counts as a behavioral test, and why a channel's own return value is never a result.
The reference used to document verb names well and their arguments barely. Three
independent sources now cover the tail of a statement, and just action-catalog --cross-check
prints them against each other:
tests/action-catalog.json— 816 vendor descriptions extracted from the app bundle (languages.zipcarries the official appendix's prose, so it is available offline). The only source that says what a parameter means.tests/attested-tails.json— 214 tails Atomix wrote into shipped skins, pad pages and the app's own compiled menu scripts, plus argument shapes (fadeout DUR DUR `BOOL`,param_bigger EXP:NUM EXP:NUM, thedeck SEL EXPwrapper) with the return type the vendor's attribute implies, for the verbs whose arguments are values rather than keywords. Attested without a probe, which reaches where no test state can.tests/binary-vocabularies.json— argument vocabularies as groups, recovered from the binary as structures (pointer tables, switch functions): 21 groups, 265 members, of which 189 are named by no per-verb source — the 26 colour names, the 19 settings pages, the 16 stem tokens. The only source that sees an enumeration matched in a shared helper rather than in the verb's own code. Leads, not confirmations.tests/verb-arg-forms.json— every candidate probed against two nonsense controls inside 10 named fixtures, because VirtualDJ silently ignores an argument it cannot parse, so a verb answering proves nothing on its own.
Agreement across all three is the strongest claim this project makes; 10 verbs have it today
(just action-catalog --cross-check).
- A misspelled argument usually makes the action do nothing, and on some verbs degrades to the bare action instead. Neither is reported as an error.
- Relative and multiplying arguments are execute-only.
loop 50%halves a loop rather than setting one, and asking a query for it errors. deck allbroadcasts on execute but collapses to deck 1 on query — the same line means two different things depending on position.timecode_cd_modelatches: script can set it and cannot unset it; only a restart clears.- The sampler's
allmeans the selected slot, not every slot.
The verb set, each verb's contract, and the grammar are now derived from VirtualDJ itself rather than assembled from documentation.
| Question | Answer | How |
|---|---|---|
Is x a verb? |
Decided, both ways | VirtualDJ's own verb table — 1,032 records / 958 distinct verbs / 62 alias groups / 38 editor-hidden on build 18.0.9598 (arm64, extracted 2026-09-05; just verb-table-stamp). Membership proves; absence disproves on that build. just verb-table <name> is the live answer |
| What category is it in? | All of them mapped | Compiled Button Editor category tables, confirmed against the live UI |
| Can it execute / query / return text? | All of them | ACTION_ class RTTI — a checked 958↔958 bijection of verbs to implementation classes. just verb-contract <name> |
| What type does it return? | 623 of 652 query verbs | Live HTTP sweep. just verb-return-type <name> |
| Does it take arguments? | 433 verbs flagged, incl. 300 with optional args | E_INVALIDARG fingerprint in each class's own methods |
| Which keyword arguments? | 262 verbs | String-comparison fingerprint — recovered get_bpm absolute, browser_window sidelist, loaded opposite and 200+ more that no documentation lists |
- A verb's value is not its truth.
get_versionreports2026and is false as a condition. 171 of 652 query verbs are traps, and no slider verb is ever true. &&is not an operator. It never guards anything; it only changes which statement's value a query reports.- Chains stop after exactly 255 statements, partially, returning
false. - GET refuses scripts over ~2,650 characters at the transport layer — which looks exactly like a language failure and caused a documented rule to be wrong for months. Use POST.
- Chained ternaries are a genuine else-if ladder; each branch takes its whole
&chain.
- Skin SDK coverage is broad; the waveform element family is in
docs/Skin Waveforms.md, and remaining element gaps are tracked mechanically (just list-xml-elements --undocumented). - Controller mapper XML is rewritten around the real
<map value="">+ device-definition split, with working mappers inexamples/Mappers/Local/. Custom device-definition XML is official-doc-derived and still not load-tested. - The VirtualDJ Remote wire protocol is decoded and proven bidirectional (docs/Remote Protocol.md).
- The plugin SDK is documented (docs/Plugin SDK.md): interface
hierarchy, the
VDJPARAM_*model and the[autoparams]manifest all 173 built-in plugins use, plugin UI models, and the interfaces present in the binary that the public headers never declare. Headers are third-party with no license grant, so they are fetched to a gitignoredvendor/rather than committed. - The native introspection channel is open (2026-08-15). The read-only plugin builds, loads,
and has swept every verb-table name through both query callbacks (
just plugin-probe <name>, captures intests/plugin-introspection*.json). It is the boundary where VDJScript results are still typed —GetInfo→double,GetStringInfo→ text — which is why HTTP flattens them, and it settled things HTTP structurally cannot:master_beat_num's float-bits defect is in the core, not the transport; a definitive channel map (532 verbs on both callbacks, 67 text-only, 11 numeric-only, 418 on neither); and the HRESULT does discriminate a recognized keyword from an ignored one, which is now the method for the keyword queue because it needs no prepared state.
- Build the verb index from the artifacts, not from prose (TODO.md task 11) —
extract_verb_index.pystill parses the 6,300-lineVDJScript Verbs.mdto producevdjscript-verb-index.json, so where the prose and the extracted evidence disagree, the prose wins silently and nothing gates it. The reconciliation diff is the real prize: every discrepancy is either a documented claim the artifacts contradict, or a curated fact the store has no field for. - Confirm the documented-but-unprobed parameters (TODO.md task 13b). The
fixture harness and argument prober are built and have run:
just fixtureslists the named states,just checkreports the probe run's verb/form/recognized totals, andjust verb-arg-forms <name>shows one verb's. What is left is the state, not the tooling —just action-catalog --cross-checknames, underdocumented_but_not_probe_confirmed, every verb whose parameters the vendor documents and no probe has confirmed, each one a state the existing fixtures never build. Itsprobe_confirmed_but_undocumentedset is the mirror worklist. - Spend the native channel on what only it reaches (TODO.md task 10a). The
plugin is built and the verb sweep is done, so the remaining value is not throughput:
GetSongBufferandOnProcessSamplesgive the actual PCM behind every waveform element;OnKey(ch, vkey, modifiers, flag, scancode)is the first channel that may expose press/release, which HTTP structurally cannot;VDJINTERFACE_SKINturns skin testing from edit-and-restart into a loop. - Behavior for most verbs is still untested — 978 of 1,038 store records carry
Untested, against 40Pass(just verb-stats). Existence, kind, category, capability and return type are settled; what a verb does mostly is not. - Audit the remaining
InferenceandCommunitylabels against docs/Evidence Standards.md, which does not permit either as a standing claim. - HTML export of the reference is parked in TODO.md and now worth doing — the per-verb pages finally have real content to show.
Contributions and corrections welcome. Corrections especially: several long-standing claims were overturned this session by re-testing them on a second channel, and the repo records retractions in place rather than deleting them.