CueSync Moving Heads
Art-Net and sACN native moving heads for Unreal Engine 5.8: seven fixtures, each with a .gdtf that
carries its geometry, its 16-bit pan and tilt and how fast they move, and validates against the
official GDTF 1.2 schema. Unreal imports the same file, so what renders and what you patch share one
channel layout. Pixel rings and matrices you address cell by cell, a gobo wheel you can see on the
deck, and one light per beam group.
Version 1.0.0. Content only: no C++, nothing to compile.
Flashing lights. Pressing Play runs the demo show, and the show strobes at up to about 21 Hz: the whole rig from 33 to 35 s, the bars' cells and the hybrids' beams between 18 and 24 s. Flashing between about 3 and 30 times a second can trigger seizures in people with photosensitive epilepsy. The heads' shutters go to 25 Hz and the pack does not limit the rate (the shutter channel is the desk's); see Flashing lights below before you show it to anyone.
Quick start
- Open the project.
Lvl_Overviewopens: one of each moving head, captioned with its mode and address. - Press Play (mind the flashing-lights warning above). The demo show (
LS_MoversDemo, 40 s) runs on the overview and onLvl_DemoStage, through the pack's own DMX path: a Sequencer DMX track writes the loopback output port, the port feeds the patches, the patches feed the fixtures. - To drive the rig from a console, open
Lvl_Patch(no show on it), press Play and send Art-Net or sACN to universes 100-108. Every fixture is captioned with its label, mode and address;fixture_manifest.json'srig(in the Tools download) lists them, and the.mvrcarries the same patch. OnLvl_DemoStage, delete or disable theCS_DemoShowactor first, or the show and your desk will fight over the same universes.
The fixtures
| Fixture | Cells | Modes (channels) | Pan: travel / speed | Tilt: travel / speed | Beams |
|---|---|---|---|---|---|
| CS Mover Wash 19 | 19 RGBW: a centre and two rings; zoom 8-30°, frost | Basic 14, Extended 88, Pixel 126 | 540° / 250°/s | 270° / 200°/s | 3 (one per ring) |
| CS Mover Wash 37 | 37 RGBW: a centre and three rings; zoom 9-36°, frost | Basic 14, Extended 160, Pixel 234 | 540° / 200°/s | 270° / 160°/s | 4 (one per ring) |
| CS Mover Beam 1 | 1 RGBW engine, 2.5° beam; zoom 2-8°, focus, iris, frost, prism | Basic 14, Extended 20 | 540° / 400°/s | 270° / 300°/s | 1 |
| CS Mover Spot 1 | 1 RGBW engine, 14° beam; zoom 8-36°, focus, iris, frost, an 8-slot gobo wheel and a rotating one, prism | Basic 16, Extended 23 | 540° / 220°/s | 270° / 180°/s | 1 |
| CS Mover Beam Ring 16 | 16 RGB ring pixels + a white 3° beam | Basic 11, Extended 59, Pixel 91 | 540° / 300°/s | 270° / 240°/s | 2 |
| CS Mover Bar 8 | 8 RGBW, 4° beams, on a 1 m tilting bar | Basic 9, Extended 39, Pixel 55 | — (tilt only) | 270° / 180°/s | 8 |
| CS Mover Matrix 25 | 25 RGBW in a 5 x 5 grid | Basic 11, Extended 109, Pixel 159 | 540° / 220°/s | 270° / 180°/s | 5 (one per row) |
Every figure — lumens, beam and field angles, speeds, weight, power — is representative of its class of fixture, not measured from a product. The designs are generic; none is modelled on or named after any manufacturer's fixture, and the gobos are plain geometry: open, dots, star, bars, ring, breakup, triangles, cross.
The modes
- Basic — the whole fixture as one: master dimmer (16-bit), shutter, 16-bit pan and tilt, zoom and frost where the head has them, the gobo wheels (the spot), then its colours (the hybrid's white drives its beam).
- Extended — master dimmer (16-bit), shutter, 16-bit pan and tilt, movement speed, the head's optics (zoom, focus, iris, frost, the gobo wheels with the second one's index and rotation, the prism with its index and rotation — whichever the head has), control; a zone's own channels (the hybrid's beam: white and its own strobe); then every cell's red, green, blue and white.
- Pixel — the same, with every cell's own dimmer and strobe after its colours.
Pan and tilt are 16-bit, coarse then fine, and home at the midpoint (32768): zero degrees is the
middle of the travel. Movement speed (PositionMSpeed) is 0 for the fixture's full speed and 255
for its slowest, stated in the .gdtf in degrees a second. The gobo wheel (Gobo1, which Unreal's
Library calls Gobo) divides the channel into eight equal steps of 32, slot 1 (Open) at 0.
Pan and Tilt follow the .gdtf. GDTF 1.2 turns a Pan axis about +Z (counter-clockwise seen from above) and a
Tilt axis about X; with these files a positive Tilt lowers the beam and a positive Pan swings it counter-clockwise from
above. Unreal's own yaw is clockwise, so the Blueprint yaws the yoke by the negative of the Pan angle (PanToYawSign).
Measured for the same DMX, in the fixture's own frame, on a standing ring and a hung wash, against the .gdtf's own
kinematics composed as GDTF 1.2 specifies: a Pan of +135 degrees lands 0.0 degrees from the file's beam (it was 90.0, the
mirror, before the sign was set) and Tilt within half a degree (verify_in_editor.py "pan sense" and "tilt sense";
verify_pack.py "tilt" pins both senses in the files). No GDTF visualiser or console has been run on these files, so
how any particular program draws them has not been seen.
The fixture shutter is ranged the way a real one is: 0-31 closed (a blackout), 32-63 open, 64-223 strobe from 0.5 to 25 Hz, 224-255 random. It powers on open. A cell's own strobe opens at zero instead — 0-9 open, 10-239 strobe, 240-255 random — so a Pixel-mode channel a desk leaves at zero never blacks a cell out. A strobe is sampled by the frame rate, so a fast one on a slow capture aliases, as a real one does on a real camera (a flash is never drawn shorter than one frame); film one at a fixed, high frame rate.
What a channel declares and what Unreal does with it. The Control channel is in the .gdtf as a
console fixture has it (0-9 no function, 10-19 reset, 20-255 reserved) so a desk can patch it, and the
Blueprint reads nothing from it: a reset sent to it does nothing in Unreal. Dimming follows the DMX value
linearly; the .gdtf declares no dimming curve and no colour-temperature (CTC/CTO) channel, and the pack
offers neither. The master dimmer is 16-bit on every head.
Flashing lights. The heads' shutters strobe at up to 25 Hz, and the pack does not limit the rate: the shutter channel is the desk's. Flashing in the range of about 3 to 30 a second can trigger seizures in people with photosensitive epilepsy, and a full-screen strobe in front of a camera is the way to hit it. Warn anyone who will watch a build of yours, and cap the rate in your own show when it will be seen by an audience you do not know.
Optics. The washes, the beam and the spot zoom and have a frost; the beam and the spot also focus, close an iris and carry a prism, and the spot adds a second, rotating gobo wheel. The hybrid, the bar and the matrix have none. A head has exactly the channels it can honour, and every later channel moves up to close the gap.
| Channel | DMX | What it does |
|---|---|---|
| Dimmer | 16-bit, 0-65535 | master, linear |
| Zoom | 16-bit, 0 narrowest | the beam angle runs from the head's narrow to its wide end (stated in the .gdtf); a narrower beam is a brighter one — the light a zoom spreads is kept, less a lens loss at the narrow end |
| Focus | 0-255, 0 sharp | softens the gobo's edge and the beam's skirt |
| Iris | 0-255, 0 open | closes both cones down to 15 % of the beam's width; the peak stays |
| Frost | 0-9 out, 10-255 in | softens the skirt and washes the gobo toward an even field; the light is kept |
| Gobo 1 | 0-255, steps of 32 | eight slots, Open first |
| Gobo 2 | 0-255, steps of 36 | Open and six patterns (vortex, fan, petals, crescent, droplets, mesh); the last slot holds to 255 |
| Gobo 2 rotation, prism rotation | 0-127 index (0-360°), 128-191 spin clockwise, 192-255 counter-clockwise | up to 180° a second |
| Prism | 0-31 open, 32-95 three facets, 96-255 five facets | each image carries 0.9 / N of the beam's light, bent 4° off the axis |
The GDTF attribute names used in these files: Pan, Tilt, PositionMSpeed, Dimmer, Zoom,
Focus1, Iris, Frost1, Gobo1, Gobo2, Gobo2Pos, Prism1, Prism1Pos, ColorAdd_R/G/B/W,
Shutter1Strobe (fixture and cell), Shutter2Strobe (the hybrid's beam), Control1, and the functions of those
channels the .gdtf names (Shutter1, Shutter1StrobeRandom, Gobo2PosRotate, Prism1PosRotate). Unreal's own GDTF
importer renames the colours to Red, Green, Blue and White and Gobo1 to Gobo; the Library in the
pack and the Blueprints use those names, so a Library you import from a .gdtf carries the same
names (the importer's renaming rule is read from the engine's own source).
How the fixtures work — BP_MoverFixture
Every fixture Blueprint is a child of Blueprints/BP_MoverFixture, which adds no C++ of its own.
Its components are the fixture: BaseMesh on the ground (or the truss), Yoke turning about the
fixture's vertical by pan, Head turning in the yoke by tilt, and on the head the face (Body) and
its lights (Beam0-Beam7). While its patch is receiving:
- Pan and tilt move, they do not jump. New DMX sets where the yoke and head are going; every
tick they travel there no faster than the fixture's own speed (the
.gdtf's travel over itsRealFade), gaining and losing speed over itsRealAcceleration— so a head starts gently and brakes onto its mark. The movement-speed channel slows both axes together. - On the tick after new DMX it reads every cell of its patch into two live textures, one texel
per cell — A: red, green, blue and the cell's dimmer; B: white, amber and the cell's
strobe — the fixture's two rows of the level's shared cell atlas (
Blueprints/BP_MoverCellAtlas, spawned by the first fixture that needs it), drawn in one canvas pass a frame. - Every tick it drives one spot light per beam group — each ring of a wash, each row of the matrix, each cell of the bar, the hybrid's ring and its beam — from that group's own cells.
- It applies the master dimmer and shutter to the face and every light; the head's zoom, iris,
frost and focus to every beam's cones and candela (the laws in the table above, the zoom's
candela from the table in the manifest); and the spot's gobo wheels to its beam: a light function
(
Materials/M_GoboLight) that projects the slot each channel selects — the second wheel turned by its rotation channel — the slide's aperture the beam's edge, softened by focus and washed out by frost. A prism adds up to four lights beside the first beam group's, each bent off its axis.
Before any DMX arrives the face runs a slow idle breathe and the head stays at home. The per-fixture
figures — cells, beam groups, candela, reach, cones, travel, speed — are variables on the child's
class defaults (category CueSync Moving Heads), written from fixture_catalogue.py.
Maps and universes
| Map | What | Universes |
|---|---|---|
Lvl_Overview | one of each fixture, widest mode, captioned; the show runs in Play | 130-131 |
Lvl_DemoStage | the 29-fixture demo rig on three trusses and the deck, in haze; the show runs in Play | 100-108 |
Lvl_Patch | the same rig, every fixture captioned; no show | 100-108 |
The demo rig is DMX/GDTF/CueSyncMovers_DemoRig.mvr, generated from the catalogue. It is MVR 1.5,
validates against the official MVR schema, embeds the pack's seven .gdtf files and holds the 29
fixtures on universes 100-108. It is drawn the way a console draws a stage (audience at -Y), so
Epic's MVR import should bring it in a quarter-turn round from Lvl_DemoStage, which already is this
rig.
The two input ports (Config/DefaultEngine.ini) listen on every interface for universes 1-179:
Art-Net on UDP 6454, sACN on UDP 5568. They are the ports every CueSync fixture pack ships, under the
same names, so a project holding several packs needs them once. This pack reserves 100-139 (the demo rig uses 100-108, the overview 130-131). Each CueSync
fixture pack numbers its demo rig's FixtureIDs from 1 (this one 1-29), so a show that patches more than
one rig should renumber the FixtureIDs when it imports them.
If nothing lights
Press Play (a fixture reads its patch on Tick). Test on Lvl_Patch. Then: the subnet (the ports
listen on 0.0.0.0), the firewall (inbound UDP 6454 / 5568), the universe (100-108 for the rig), and
the shutter — at DMX 0-31 the fixture is closed by design. A head that points at the floor: pan and
tilt at DMX 0 are the ends of their travel, not home; home is 32768.
Performance
Measured 2026-10-03 on this build (the lights reach at least 21 m, the heads have their optics and redrawn housings).
The demo stage — 29 moving heads, 95 beam-group spot lights (plus 28 hidden ones, the prism pool of the seven spots and
beams), a gobo light function on each spot, volumetric haze, the show running — at 1920 x 1080 in a windowed -game
session on an Apple M4 Pro (the engine's CSV profiler, 900 frames, the median of frames 400-900):
| Map | Frame | Game thread | GPU | GPU in passes the profiler names |
|---|---|---|---|---|
Lvl_DemoStage (first capture, load average 2.8) | 27.6 ms (36 fps) | 8.5 ms | 27.4 ms | 8.0 ms (haze 2.4, lights 1.9, deferred lighting 1.1) |
Lvl_DemoStage (second capture, other work running: load average 11-20) | 27.3 ms (37 fps) | 7.0 ms | 26.9 ms | 8.1 ms |
Lvl_Overview (7 heads, Lumen on; one capture, load average 15) | 18.7 ms (53 fps) | 3.7 ms | 18.5 ms | 10.0 ms |
The previous build (2026-09-29) measured 26.1 ms (38 fps) on the demo stage: 1.5 ms more now, with the lights reaching further. The demo stage is GPU-bound, which is why a busy machine did not move the figure between the two captures; the overview's 18.7 ms shows it does move with the scene. That is what one machine measured — a range, not a promise of what yours will do. An empty engine map through the same capture ran at 8.3 ms (GPU 6.4 ms, 6.1 ms of it in named passes; measured 2026-09-29), so the difference is the stage.
What keeps the cost down, and what to turn if your rig is bigger — the defaults CueSync Strobe & Blinder measured, which this pack keeps:
| Default | Why |
|---|---|
| One cell atlas a level | every fixture's live textures are rows of one render target, drawn in one canvas pass a frame |
| Redraw only on new DMX | cells are re-read only when the patch changes; the axes and the lights update every tick |
| Light radius 25 m | a light shades every pixel inside its radius. A moving head is so bright that 1 lux is 94-1,531 m away, so every light of this pack sits at the 25 m cap and a beam stops there (14-3,752 lux are still falling on a surface at 25 m); no light is ever under 21.1 m (where it keeps 80 % of its light at a 12 m throw). group_reach in the vendored factory decides it |
| Lumen off on the demo stage | its light is direct; the stage's post-process volume sets GI to None (the project keeps Lumen for your maps) |
| No shadows from fixture lights | 95 shadow-casting spots would dominate everything |
| Haze on, froxels 8 px | the look; r.VolumetricFog.GridPixelSize 16 is cheaper, 4 draws narrow beams without steps (the gallery stills use 4) |
Measure your own rig before you assume any of these. The harness behind these figures
(Tools/perf_run.sh) is the seller's bench and does not ship in this download: it runs a map in
-game with -csvCaptureFrames=900 -csvGpuStats and takes the medians from the CSV the engine
writes under Saved/Profiling/CSV (with the launcher engine on a Mac,
~/Library/Application Support/Epic/UnrealEngine/5.8/). stat unit in a Play session gives a
quicker reading.
The tooling (the Tools download)
The scripts are not in the project download. Fab does not allow Python files in a content project's zip,
so they ship as the listing's Additional Files download, CueSyncMovers_Tools_<version>.zip: unzip it
over the project folder and it lands in this Docs/ folder (its TOOLS.md says what it holds). Nothing
in the project needs it to run. The rest of this section describes what that download adds.
Standard-library Python. fixture_catalogue.py is every fixture as data; build_fixtures.py writes
the .gdtf files, the .mvr, fixture_manifest.json and the gobo atlas through cuesync_factory/
(the series' shared writer, vendored by commit); the in-editor scripts build the Library, materials,
meshes, Blueprints, maps and show from them. Two verifiers:
python3 verify_pack.py— nineteen checks outside the editor, among them that Tilt moves every beam by the angle it is given, that every cell sits where the manifest puts it, that no light reaches under 21 m and that the attribute names are the ones Unreal's own importer keeps and that the download has Fab's shape (--selftestmakes each one fail once);verify_in_editor.py— twenty checks inside it, thirteen of them live in Simulate: pan and tilt against the.gdtf's travel on a standing and a hung head, the Blueprint's Tilt against the.gdtf's own kinematics, the axis speed and its easing, the ring cells read back with the head turned, each beam group's light, the gobo wheel, the shutter blackout, the strobe rate and its one-frame floor, and every optics channel through the Library: the 16-bit dimmer's fine byte, zoom, focus, iris, frost, both gobo wheels with the second one's rotation, and the prism. Run it from Tools → Execute Python Script.
The in-editor scripts need the engine's Python Editor Script Plugin (PythonScriptPlugin), which
this project enables and which is a beta plugin in UE 5.8; nothing else in the pack needs it.
What runs in this download, and what is the seller's bench. Three things these scripts mention
do not ship: Tools/ (the seller's shell harnesses — perf_run.sh, author_base.sh,
verify_in_editor.sh, ue_run.sh), Content/CueSyncMoversDev/ (the mesh template and the gallery
maps) and CueSyncPackFactory (the series' generator, which cuesync_factory/ is vendored from).
So:
verify_pack.py,verify_in_editor.pyandbuild_fixtures.pyneed nothing that is missing here.verify_pack.py's strobe-tables check reads one more copy of the shutter ranges from the authoring commandlet's source when aCueSyncPackFactorycheckout sits beside the project; here there is none, so that one comparison is skipped and the rest of the check runs.build_meshes.pybuilds nothing and reports "no /Game/CueSyncMoversDev/SM_MeshTemplate": it builds by duplicating that template, which stays on the seller's bench. The meshes ship built.build_fixture_blueprints.pyconfigures the fixtures on the shippedBP_MoverFixture; re-authoring that base isTools/author_base.sh, which does not ship.- The scripts' docstrings name
Tools/wrappers for headless runs; in this download, run the in-editor scripts from Tools → Execute Python Script.
One pack per Python session. These scripts are this pack's own, but they share their module
names with the other CueSync fixture packs' — fixture_catalogue, build_fixtures, verify_pack,
verify_in_editor and the package cuesync_factory (CueSync Pixel ships the first three too). Each
script puts its own folder first on sys.path, so in one Python interpreter every script gets the
modules of whichever pack ran first. Run CueSync Strobe & Blinder's build_fixtures.py and then
this pack's in the same interpreter, and this pack's stops with
ImportError: cannot import name 'gobos' from 'cuesync_factory'. In the other order, this pack's
first, Strobe & Blinder's script reads this pack's catalogue and refuses to write (every fixture is
outside its RDM range and name prefixes). The Unreal editor keeps one interpreter for the whole
session, so in a project that holds two packs, restart the editor before running the other pack's
scripts. From a shell, each python3 run is a fresh interpreter and is safe.
What the pictures on the listing show
Every gallery plate is a frame of Lvl_DemoStage or Lvl_Overview from this download: the heads, the three
truss runs, the deck and the haze are in it. These are not: the cameras, each shot's exposure, fog density and an
added key light; the still look each plate was lit with (the download's show is LS_MoversDemo); and plate 04's
arrangement, seven heads spread over one frame and cut into a captioned sheet. No plate shows a crowd, set steel or
LED screens. The five plates were shot on the 2026-09-29 build, before the heads were redrawn, so they show the
earlier box-shaped heads; the listing says so until they are re-shot.