This document gives a high-level overview of the concepts used in the JSON format of the fixture definitions collected in the Open Fixture Library. Those fixture definitions (also called personalities or profiles) specify information about a fixture's DMX controlment and general attributes.
Note: The fixture format is not intended to be used directly by other software, as it may introduce breaking, not-backwards-compatible changes at any time. Instead, write a plugin to transform the data into a more stable format for your application. Internally in OFL, working with the fixture model is preferred, as it eases access to the fixture data.
The JSON Schema files can be found in the schemas/
directory. JSON Schema is a declarative way to describe allowed JSON properties and values. The fixtures-valid.js
test automatically checks the fixtures against these schemas and additionally tests things like the correct use of channel keys etc. programmatically.
The schema files have a version
property. Every time the schema is updated, this version needs to be incremented in all schema files using semantic versioning.
Given a version number MAJOR.MINOR.PATCH, increment the:
- MAJOR version when you make incompatible schema changes.
i.e. old fixtures are not valid with the new schema anymore. - MINOR version when you add functionality in a backwards-compatible manner.
i.e. old fixtures are still valid with the new schema, new fixtures aren't valid with the old schema. - PATCH version when you make backwards-compatible bug fixes.
e.g. an upper bound to an integer value is added, which was likely done right in the past anyway.
The JSON fixture format is intended to be
- readable by both humans and machines
- easily extendable
- as general as possible to include information for many fixture formats
- abstract where possible, specific where needed
The manufacturer of a fixture is determined by its toplevel directory (relative to the fixtures
directory), the fixture key is the filename without extension. Manufacturer data is stored in fixtures/manufacturers.json
.
fixtures/register.json
(which is generated by cli/make-register.js
) is just an index to make searching specific attributes possible without having to read the whole directory structure, which makes it faster. This file is gitignored.
A fixture's shortName
must be unique amongst all fixtures.
The physical
section describes properties not directly used in the DMX protocol, but often in lighting control software to display a preview of the fixtures in action.
A fixture can have multiple modes (also sometimes called personalities) like "Basic 3-channel mode" or "Extended 5-channel mode". Our modes are not allowed to have the word "mode" in them, as it is automatically appended at the end.
A mode can contain the physical
property to override specific physical data of the fixture. E.g. one mode could set the power
value different than the fixture default.
A mode's shortName
must be unique amongst all modes of the respective fixture.
All channels that are used in one or more modes are listed in availableChannels
. The modes then only contain a list of the channel keys.
A channel's defaultValue
is the DMX value that this channel is set to without the user interacting (most often, this will be 0
, but e.g. for Pan and Tilt channels, other values make more sense). Likewise, highlightValue
specifies the DMX value if a user highlights this channel (defaults to the maximum DMX value). This is not available in every software.
If constant
is true
, the channel should be set to a static value in the operating lighting program. precedence
specifies to which value the channel should be set if there are two conflicting active cues containing this channel: HTP (Highest takes precedence) or LTP (Latest (change) takes precedence).
A channel can do different things depending on which range its DMX value currently is in. Those ranges that can be triggered manually in many programs are called capabilities. Choose a type
to declare which property of the fixture is changed by this capability, e.g. ShutterOpen
, Intensity
or Pan
. Depending on the type, there exist more properties that further describe the capability, like the pan angle, the strobe rate or the wheel slot number. Most of these are physical entities that require to be entered using specific units (like "10.5Hz"
or "100%"
). Some entities offer keywords to replace specific percentage values, e.g. Distance: "near"
= "1%"
, "far"
= "100%"
. See the full list of units, entities and capability types with their properties. Example:
"availableChannels": {
"Shutter": {
"capabilities": [
{
"dmxRange": [0, 20],
"type": "ShutterStrobe",
"shutterEffect": "Closed",
"comment": "Blackout"
},
{
"dmxRange": [21, 255],
"type": "ShutterStrobe",
"shutterEffect": "Open"
}
]
}
}
If a channel consists of a single 0-255 capability, one should use the capability
property (instead of capabilities
), which only needs a single object instead of an array of objects. The dmxRange
is implicit in this object (see example below).
Capabilities may be steps, which means that the whole DMX range has the same effect (e.g. "Gobo 1"), or proportional, which means that the effect is increasing / decreasing from the start to the end of the DMX range (e.g. "Intensity 0-100%"). A proportional capability can define a start and an end value of its type-specific properties. Example:
"availableChannels": {
"Pan": {
"capability": {
"type": "Pan",
"angleStart": "0deg",
"angleEnd": "530deg"
// DMX value 0 -> 0°
// DMX value 127 -> 264°
// DMX value 255 -> 530°
}
}
}
Please note that some properties don't allow start/end values, for example hold
.
The menuClick
property defines which DMX value to use if the whole capability is selected: start
/ center
/ end
sets the channel's DMX value to the start / center / end of the range, respectively. hidden
hides this capability from the trigger menu. This is one of those special features that are supported only by some lighting programs.
A channel can list fineChannelAliases
to specify which channel keys are used to describe its finer variants. This results in two or more (8 bit) channels being combined into one 16 bit (or 24 bit, ...) channel to increase the resolution of the controlled functionality.
Example: Channel Dimmer
contains fineChannelAliases: ["Dimmer 16-bit", "Dimmer 24-bit"]
. Mode "Normal" uses only Dimmer
in its channel list, mode "Fine" uses Dimmer
and Dimmer 16-bit
, mode "Super fine" uses all three.
See the Generic Desk Channel fixture for a simple application example.
DMX values (e.g. default / highlight value, capability ranges) have to be entered in the maximum resolution: If one fine channel is defined, DMX values from 0 to 65535 are possible. If a lower resolution is sufficient for entering DMX values, the channel's dmxValueResolution
property can be set to the desired resolution (8bit
for 0…255, 16bit
for 0…65535, etc.). That means that both of the following examples are equivalent:
"Iris": {
"fineChannelAliases": ["Iris fine"],
"defaultValue": 33792,
"capabilities": [
{
"dmxRange": [0, 33791],
"type": "Iris",
"openPercentStart": "100%",
"openPercentEnd": "4%"
},
{
"dmxRange": [33792, 65535],
"type": "Iris",
"openPercent": "4%"
}
]
}
"Iris": {
"fineChannelAliases": ["Iris fine"],
"dmxValueResolution": "8bit",
"defaultValue": 132,
"capabilities": [
{
"dmxRange": [0, 131],
"type": "Iris",
"openPercentStart": "100%",
"openPercentEnd": "4%"
},
{
"dmxRange": [132, 255],
"type": "Iris",
"openPercent": "4%"
}
]
}
A switching channel is a channel whose functionality depends on the value of another channel in the same mode.
E.g. in a given mode, the first channel could be used to select auto-programs and channel 2 could be either "Microphone Sensitivity" (if channel 1 is set to Sound control) or "Program Speed" (if channel 1 is set to anything else).
To define switching channels, add a switchChannels
object to all capabilities of the dependency channel (the "Auto-Programs" channel in the example above). This object defines which switching channel alias is set to which available channel key if this capability is active. The switching channel alias is then used in the mode just like a regular channel. Note that a channel which defines switching channels needs an explicit defaultValue
to make sure that the switching channel default is also well-defined.
See the Futurelight PRO Slim PAR-7 HCL fixture for a simple application example.
Some fixtures have multiple light beams: A horizontal bar of LEDs, a pixel head with a grid of lamps, a fixture consisting of inner and outer rings of LEDs that can be controlled separately, etc. See the Eurolite LED KLS 801 and the Pixel Bar and Matrix categories for example fixtures.
The information how these pixels are arranged is stored in the fixture's matrix
object: Either by using the x × y × z syntax from pixelCount
(e.g. [5, 5, 1] for a 5×5 matrix) or by naming each individual pixel in pixelKeys
, e.g.:
"matrix": {
"pixelKeys": [
[
[ null, "Top", null ],
["Left", "Center", "Right"],
[ null, "Bottom", null ]
]
]
}
null
refers to a "hole", i.e. there's no light beam, which allows for non-cubic frames. The above example represents 5 heads arranged like a "+".
Pixels can also be grouped if a fixture allows control in different fine grades, like fourths or halves of a light bar. There are three ways to define a pixel group:
- The keyword
all
indicates that all pixel keys should belong to this group. This can be useful for channels like "Red Master". - An array of pixel keys exactly describes which pixels belong to this group.
- An object with
x
,y
,z
orname
constraints. All pixels that fulfill all constraints belong to this group. XYZ constraints are<=5
,=5
,>=5
,3n
(divisible by 3),3n+1
(divisible by 3 with remainder 1),even
(≙2n
) andodd
(≙2n+1
). Name constraints are regular expressions.
"matrix": {
"pixelCount": [12, 1, 1],
"pixelGroups": {
"Master": "all",
"1/3": { "x": ["<=4"] },
"2/3": { "x": [">=5", "<=8"] },
"3/3": { "x": [">=9"] },
"Outer Thirds": ["1", "2", "3", "4", "9", "10", "11", "12"]
}
}
See the TMB Solaris Flare for a rather complex example of pixel group constraints.
Pixel groups can also be used to better describe the pixel structure, for example to define circular rings consisting of virtual pixels, even if these pixels don't physically exist and only the whole rings can be controlled.
"matrix": {
"pixelKeys": [
[
[null, null, "O1", "O2", null, null],
[null, "O3", "M1", "M2", "O4", null],
["O5", "M3", "I1", "I2", "M4", "O6"],
["O7", "M5", "I3", "I4", "M6", "O8"],
[null, "O9", "M7", "M8", "O10", null],
[null, null, "O11", "O12", null, null]
]
],
"pixelGroups": {
"Inner ring": { "name": ["^I[1-4]$"] },
"Middle ring": { "name": ["^M[1-8]$"] },
"Outer ring": { "name": ["^O\\d+$"] }
}
}
To reuse similar channels for each pixel or pixel group (like "Red 1", Red 2", ...), add template channels: They are specified very similar to normal available channels, except that each template channel key / alias / name must contain the $pixelKey
variable:
"templateChannels": {
"Red $pixelKey": {
"fineChannelAliases": ["Red $pixelKey fine"],
"capability": {
"type": "ColorIntensity",
"color": "Red"
}
}
}
Template channels can also introduce fine and switching channels. Specific resolved matrix channels can be overridden by available channels (e.g. if "Speed 1" has different capabilities than "Speed 2" until "Speed 25"). See the cameo Hydrabeam 300 RGBW that uses these features.
Then, either use the resolved channel keys directly in a mode's channel list, or use a matrix channel insert block that repeats a list of template channels for a list of pixels:
{
"name": "14-channel",
"shortName": "14ch",
"channels": [
"Master Dimmer",
"Strobe",
{
"insert": "matrixChannels", // static value for matrix channels
"repeatFor": "eachPixelXYZ", // see below
"channelOrder": "perPixel", // or "perChannel"
"templateChannels": [
"Red $pixelKey",
"Green $pixelKey",
"Blue $pixelKey"
]
}
]
}
repeatFor
defines in which order and for which pixels / pixel groups the template channels shall be repeated. Possible values are:
- An array of pixel (group) keys in the proper order
"eachPixelABC"
: Gets computed into an alphanumerically sorted list of all pixelKeys"eachPixelXYZ"
/"eachPixelZYX"
/ ...: Gets computed into a list of all pixelKeys, sorted by position, depending on the usedX
/Y
/Z
combination.- For example,
XYZ
orders the pixels like reading a book (latin script): First left-to-right (X
, letter by letter), then top-to-bottom (Y
, line by line), then front-to-back (Z
, page by page). For a 3-dimensional 2×2×2 matrix, this results in["(1, 1, 1)", "(2, 1, 1)", "(1, 2, 1)", "(2, 2, 1)", "(1, 1, 2)", "(2, 1, 2)", "(1, 2, 2)", "(2, 2, 2)]"
.
- For example,
"eachPixelGroup"
: Gets computed into an array of all pixel group keys, ordered by appearance in the JSON file.- For the above matrix structure example, this results in
["Inner ring", "Middle ring", "Outer ring"]
.
- For the above matrix structure example, this results in
Fixtures (usually moving heads) can have internal wheels, where you can select the active slot via DMX. In our fixture format, wheels are defined in the fixture's wheels
object, where the key defines the wheel's name.
The slots in a wheel have types, similar to capability types. Depending on the type, different properties can be set on the slot.
Open
/Closed
Color
name
(string)colors
(array of hex strings)colorTemperature
(Entity ColorTemperature)
Gobo
resource
(resource reference string, see below)name
(string)
Prism
name
(string)facets
(integer)
Iris
openPercent
(Entity IrisPercent)
Frost
frostIntensity
(Entity Percent)
AnimationGoboStart
name
(string)
AnimationGoboEnd
Animation Gobo slots are wider than normal gobos (sometimes they fill the whole wheel); rotating the wheel over these slots creates an animation. To model the wider slots, an AnimationGoboEnd
slot must be used directly after an AnimationGoboStart
slot.
Gobos are referenced with a resource reference in the form gobos/<gobo key>
.
Gobo resources are stored in the resources/gobos/
directory. Each one consists of a JSON file (<gobo key>.json
) describing the gobo (with name, keywords, and a source where the gobo image was extracted from) and the gobo image itself (<gobo key>.svg
or <gobo key>.png
).
In the resources/gobos/aliases/
directory, sets of aliases can be defined as separate JSON files in which aliases are mapped to gobo keys. This is useful for plugins (e.g. the QLC+ import plugin knows which OFL gobo key to use when QLC+ gobo Others/0001.svg
is referenced in a fixture). It also enables referencing gobos in fixture files with an alias like gobos/aliases/<alias file>/<alias key>
(e.g. a Robe fixture could reference gobos/aliases/robe/15020246-rafia
) to make validating the gobo information easier with a manual where product numbers are specified.
In wheel-related capabilities, the wheel
property references the wheel by its name. If the wheel
property is not set, the channel name is used as wheel name.
WheelSlot
capabilities select a slot from the wheel. If the capability selects the place in between two slots, the slotNumber
property can be set to a fractional value (or be proportional as slotNumberStart
/ slotNumberEnd
). See also footnote slotNumber in the capability types documentation.
WheelShake
capabilities set the shaking (i.e. continuously rotating back and forth) of the whole wheel around the currently selected slot. A slot can also be activated directly by setting the slotNumber
property like in WheelSlot
capabilities.
By setting the property isShaking
to slot
, one can specify that only the currently selected slot rotates back and forth around its center (sometimes called Gobo bounce effect) instead of the whole wheel.
WheelSlotRotation
capabilities control the rotation of the currently selected slot (i.e. Gobo, Prism, etc.). A slot can also be activated directly by setting the slotNumber
property like in WheelSlot
capabilities.
WheelRotation
capabilities rotate the whole wheel, i.e. over all slots.
We link to Open Lighting's RDM database if possible. Thus, we need to specify the RDM manufacturer ID per manufacturer and the RDM model ID per fixture. Additionally, each mode is mapped to the respective RDM personality via the rdmPersonalityIndex
property. To ensure compatibility, we also track, for which RDM fixture software (firmware) version the mode indices are specified.
If RDM manufacturer and model ID are known, we open the respective fixture page when going to /rdm
(handled in main.js
and views/pages/rdm-*.js
).
Fixtures may be renamed by their manufacturers. If we would just update the fixture definition, links to its old fixture page would now lead to a 404 Not found error. Instead, we add a fixture redirect JSON file (see its schema) with the old manufacturer / fixture key, pointing to the updated fixture definition. See Minuit Une M-Carré (and its fixture page on OFL) as an example.
Another use case for redirects are fixtures that are sold under different brands, but which are technically the same. We add them to the library only once and let redirects with other names point to it.