node-dmx is a TypeScript library for controlling DMX over IP from Node.js.
It supports:
| Area | Feature | sACN (E1.31) | Art-Net 4 | RDMnet (E1.33) | Notes |
|---|---|---|---|---|---|
| Core I/O | DMX Send | ✅ | ✅ | ❌ | DMXController sends DMX via sACN/Art-Net |
| Core I/O | DMX Receive / Listen | ✅ | ✅ | ✅ | Receiver, ArtNetReceiver, RdmnetClient |
| Device Discovery | Controller/Node Discovery | ❌ | ✅ | ⚠️ Partial | ArtNetDiscovery, RdmnetDiscovery (DNS-SD + mDNS) |
| Device Management | RDM Messaging | ❌ | ✅ | ⚠️ Partial | ArtNetRdmClient full flow, RdmnetClient supports broker session + RPT rdmTransaction |
| Security | Secure Transport | ❌ | ❌ | ✅ | RdmnetClient supports transport: 'tls', peer auth policy, post-connect auth hook |
| Ecosystem | Fixture Plugin System | ✅ | ✅ | ❌ | Fixture API currently targets DMX output protocols |
| Compliance | Formal Protocol Compliance Claim | ✅ E1.31 implementation | ✅ Art-Net implementation | ⚠️ In progress | RDMnet full claim depends on third-party interop/conformance runs |
This guide is written for students and first-time users: start simple, then move to advanced features.
npm install node-dmx
flush() to send them.import {DMXController} from 'node-dmx';
const controller = new DMXController({protocol: 'sacn'});
const universe = controller.addUniverse(1);
universe.setChannel(1, 255); // channel 1 at full
await controller.flush();
controller.close();
import {DMXController} from 'node-dmx';
const controller = new DMXController({
protocol: 'artnet',
artSync: true,
artnet: {
host: '255.255.255.255',
broadcast: true,
},
});
controller.setChannel(1, 1, 255); // universe 1, channel 1, full
await controller.flush();
controller.close();
import {DMXController} from 'node-dmx';
const controller = new DMXController({protocol: 'sacn'});
const u1 = controller.addUniverse(1);
const u2 = controller.addUniverse(2);
u1.setChannel(1, 255);
u2.setChannel(1, 128);
await controller.flush(); // sends all dirty universes
import {ArtNetDiscovery} from 'node-dmx';
const discovery = new ArtNetDiscovery();
const replies = await discovery.pollOnce();
for (const node of replies) {
console.log(node.ip, node.shortName, node.longName);
}
discovery.close();
import {ArtNetRdmClient, RdmCommandClass, PIDS} from 'node-dmx';
const client = new ArtNetRdmClient({host: '192.168.0.10'});
const response = await client.rdmTransaction(1, {
destinationUid: {manufacturerId: 0x7a70, deviceId: 0x00000001},
sourceUid: {manufacturerId: 0x7a70, deviceId: 0x00000002},
transactionNumber: 1,
portId: 1,
subDevice: 0,
commandClass: RdmCommandClass.GET_COMMAND,
pid: PIDS.DEVICE_INFO,
});
console.log(response);
client.close();
import {DMXController, Fixture, RGBDimmerFixture} from 'node-dmx';
const controller = new DMXController({protocol: 'sacn'});
controller.addUniverse(1);
const fixture = new Fixture(
controller,
RGBDimmerFixture,
1, // universe
1, // start address
'4ch', // personality id
);
// RGBDimmerFixture expects state with `dimmer` and `rgb` array values in range 0..1
fixture.set({dimmer: 1, rgb: [1, 0.25, 0.1]});
fixture.render();
await controller.flush();
Available built-in example fixture plugins:
RGBDimmerFixture (4ch: dimmer + RGB)RGBWW5Fixture (5ch: R + G + B + warm white + cool white)All examples live in examples/.
Core examples:
examples/sacn-basic.tsexamples/sacn-animate.tsexamples/sacn-rainbow.tsexamples/artnet-animate.tsexamples/artnet-listen-matrix.tsexamples/artnet-discover.tsexamples/rdm-tod.tsexamples/rdmnet-listen.tsexamples/rdmnet-discover.tsexamples/rdmnet-interop-smoke.tsexamples/fixture-rgbww5.tsAdditional animation examples:
examples/sacn-comet-chase.tsexamples/sacn-fire-flicker.tsexamples/sacn-police-strobe.tsexamples/sacn-breathing-pastels.tsexamples/sacn-color-wipe.tsexamples/sacn-sparkle.tsexamples/sacn-theater-chase.tsexamples/sacn-aurora.tsexamples/sacn-pulse-stack.tsexamples/sacn-storm.tsexamples/sacn-halloween.tsexamples/sacn-christmas.tsRun an example:
npm run example/sacn-rainbow
Useful options for DMXController:
protocol: 'sacn' | 'artnet'iface: local interface IPv4 addressreuseAddr: allow multiple listeners/senders on same UDP portunicastDestination: send unicast instead of multicast/broadcastartSync: send ArtSync after flush (Art-Net)RDMnet client options:
transport: 'tcp' | 'tls' (default tcp)tls: Node TLS options (CA, client cert/key, ciphers, min/max version, etc.)requireTlsAuthorization: reject unauthorized TLS peers (default true when transport: 'tls')postConnectAuth: async hook for environment-specific auth/profile checksUse real brokers/devices for compliance verification (not mocks only).
Smoke-runner against a real broker:
RDMNET_INTEROP_HOST=192.168.1.50 \
RDMNET_INTEROP_PORT=5568 \
npm run example/rdmnet-interop-smoke
Environment-gated integration test:
RDMNET_INTEROP_HOST=192.168.1.50 \
RDMNET_INTEROP_PORT=5568 \
RDMNET_INTEROP_SCOPE=default \
npm run test:interop:rdmnet
Useful flags:
RDMNET_INTEROP_TLS=1: use TLS transportRDMNET_INTEROP_TLS_STRICT=0: allow non-authorized TLS peer for lab setupsRDMNET_INTEROP_CHECK_LISTS=1: also validate client/endpoint list requestsRDMNET_INTEROP_TIMEOUT_MS=5000: per-request timeout overrideTypeDoc output uses README.md as the landing page.
Build docs:
npm run docs:build
Output:
generated/docsiface) is on the same subnet as the fixture.