Schematic KDL
Glossary
- Top-level nodes:
panelvariants,object_3d,line_3d,vector_arrow,window. - EQL: expressions are evaluated in the runtime EQL context. Vector-like fields expect 3 components;
world_posis a 7-component array (quat + position). - Colors:
color r g b [a]or named (black,white,blue,red,orange,yellow,yalk,pink,cyan,gray,green,mint,turquoise,slate,pumpkin,yolk,peach,reddish,hyperblue); alpha optional. Colors can be inline or incolor/colourchild nodes. Defaults to white when omitted unless noted.
theme
- Optional top-level node that sets the session UI appearance.
mode:"dark"(default) or"light"; drives window decorations and picks the dark/light variant of the color scheme. If a preset does not ship a light variant, the theme stays in dark.scheme: name of a color preset. Built-ins aredefault,eggplant,catppuccini-macchiato,catppuccini-mocha,catppuccini-latte, andmatrix; user presets are picked up from anycolor_schemesfolder in the asset directory or data directory. Unknown names fall back todefault. If a user preset shares a name with a built-in, the user version wins. See color-schemes for the file layout.- Applies to the whole session; a secondary file can set its own
modefor its windows, but the active scheme stays the one from the primary schematic. - Controls both egui styling (palette) and the window decoration theme (Dark/Light).
window
path/file/name: optional secondary schematic file. Relative paths resolve against the parent schematic directory (or CWD). If absent, the entry configures the primary window instead of loading a secondary file.title/display: optional window title.screen: optional zero-based display index.rect { x y width height }: optional child; four percentages (clamped 0–100). Used for placement of primary or secondary windows. Secondary windows default toDEFAULT_SECONDARY_RECTwhen unset.
panel containers
tabs { ... }: children are panels. No extra props.hsplit/vsplit: children are panels. Childshare=<f32>controls the weight within the split.active(bool) is parsed but not currently used. Optionalname.
panel content
viewport:fov(default 45.0), optionalnear/farclipping planes (if omitted, camera defaults arenear=0.05andfar=5.0; if set, they are applied to the camera projection), optionalaspect(if omitted, ratio is derived from viewport size),active(bool, default false),show_grid(default false),show_arrows(default true),create_frustum(default false; creates that viewport camera frustum),show_frustums(default false; shows frustums created by other viewports on this viewport),frustums_color(defaultyellow),frustums_thickness(default0.006world units),show_view_cube(default true),hdr(default false),name(optional label), camerapos/look_at(optional EQL). Vector arrows can also be declared directly inside the viewport node; those arrows are treated as part of that viewport’s layer and respect itsshow_arrows/show_gridsettings, allowing you to build a local triad tied to the viewport camera. Anup(default "(0, 1, 0)") specifies a direction vector in the world frame for the camera.graph: positionaleql(required),name(optional),type(line/point/bar, defaultline),lock(default false),auto_y_range(default true),y_min/y_max(default0.0..1.0), childcolornodes (optional list; otherwise palette).component_monitor:component_name(required),name(optional).action_pane:name(required pane title),luascript (required).query_table:name(optional), positionalquery(defaults to empty),type(eqldefault, orsql).query_plot:name(required pane title),query(required),refresh_intervalin ms (default 1000),auto_refresh(default false),color(default white),type(eqldefault, orsql),mode(timeseriesdefault, orxyfor numeric X-axis labels),x_label(optional X-axis label for XY mode),y_label(optional Y-axis label).data_overview:name(optional pane title).schematic_tree:name(optional pane title). (Hierarchy/Inspector sidebars are implicit and not serialized.)video_stream: positionalmsg_name(required; the message name matching theelodinsinkmsg-nameproperty),name(optional display label; defaults to"Video Stream <msg_name>"). Displays an H.264 video stream received by Elodin DB. The video source can be a GStreamer pipeline usingelodinsink, an OBS Studio SRT stream via a receiver pipeline, or any source that sends H.264 NAL units to Elodin DB. See the OBS Studio Integration section below.dashboard: layout node (Bevy UI style). Key properties:name(optional),display(flexdefault, orgrid/block/none),box_sizing(border-boxdefault orcontent-box),position_type(relativedefault orabsolute),overflow(per-axis; defaults visible),overflow_clip_margin(visual_box + margin, defaults content-box / 0), sizing (left/right/top/bottom/width/height/min_*/max_*acceptauto,px,%,vw,vh,vmin,vmax; defaultauto),aspect_ratio(optional f32), alignment (align_items/justify_items/align_self/justify_self/align_content/justify_content, all default todefaultvariants), flex (flex_direction,flex_wrap,flex_growdefault 0,flex_shrinkdefault 1,flex_basisdefaultauto,row_gap/column_gapdefaultauto),children(nested dashboard nodes), colors viabg/backgroundchild (default transparent),text(optional),font_size(default 16),text_colorchild (default white), spacing viamargin/padding/borderchildren withleft/right/top/bottom.
object_3d
-
Positional
eql: required. Evaluated to aworld_pos-like value to place the mesh. -
Mesh child (required, exactly one):
glb:path(required),scale(default 1.0),translate(x,y,z)(default 0s),rotate(deg_x,deg_y,deg_z)in degrees (default 0s).animatechild nodes (optional, multiple): For rigged GLB models, animate specific joints/bones.joint: required string; the exact name of the joint/bone in the GLB file.rotation_vector: required EQL expression; must evaluate to a 3-element vector(x, y, z)where:- The vector direction is the rotation axis.
- The vector magnitude is the rotation angle in degrees.
- Example:
animate joint="Root.Fin_0" rotation_vector="(0, rocket.fin_deflect, 0)"
sphere:radius(required);color(default white).box:x,y,z(all required);color(default white).cylinder:radius,height(both required);color(default white).plane:width/depth(defaultsizeif set, else 10.0); optionalsizeshorthand;color(default white).ellipsoid: ,color(default white),show_grid(default#false).- Physical measure
-
scalean EQL string, e.g.,"(1, 1, 1)"in meters - Error measure
error_covariance_choleskyas an alternative to specifying the scale and rotation, one can specify the lower triangle cholesky L of the error covariance matrix P = LL^T. Example"(a,b,c,d,e,f)"which describes a matrix that looks like this: | a 0 0 | | b c 0 | | d e f |error_confidence_interval(default70) the percentage that if this were repeated 100 times, we would expect that in 70 cases, the true value would be within the bounds. In practice this means that the larger the error confidence interval, the larger the ellipsoid.
- Physical measure
-
Mesh nodes support an optional
emissivity=<value>property (0.0–1.0) to make the material glow (e.g.,sphere radius=0.2 emissivity=0.25 { color yellow }).Mesh nodes and
iconnodes both support an optionalvisibility_rangechild node that controls at what camera distances the element is rendered:visibility_range: child node withmin(default 0) andmax(default infinity) properties. The element is visible when the camera distance is betweenminandmax. Both mesh and icon are visible at all distances by default;visibility_rangeis purely opt-in.fade_distance(icon only, default 0): world-unit distance over which the icon fades in at theminboundary and fades out at themaxboundary. For example,min=50 fade_distance=50means the icon starts appearing at distance 50 (alpha=0) and reaches full opacity at distance 100. Mesh nodes use hard visibility cutoffs at theirmin/maxboundaries.- Ranges can overlap (both mesh and icon visible simultaneously) or have gaps.
-
iconchild (optional): Displays a fixed-size billboard icon at the object's position. Each viewport camera independently evaluates whether to show the icon based on its own distance. The icon always faces the camera and maintains a constant screen pixel size.- Source (exactly one required):
builtin: name of a Material Icons glyph (snake_case). Supported names include:satellite_alt,satellite,rocket_launch,rocket,flight,flight_takeoff,public,language,circle,fiber_manual_record,star,star_outline,location_on,place,adjust,gps_fixed,my_location,explore,navigation,near_me,diamond,hexagon,change_history,lens,panorama_fish_eye,radio_button_unchecked,brightness_1,flare,wb_sunny,bolt.path: path to a custom PNG image file (loaded from the assets folder).
colorchild node: tint color for the icon using the standardcolor r g b [a]format or named colors (default white). See Colors in the glossary above.visibility_rangechild node:min,max, andfade_distancein world units (see above).size: desired screen pixel size of the icon (default 32).
- Source (exactly one required):
line_3d
- Positional
eql: required; expects 3 values (or 7 where the last 3 are XYZ). line_width: default 1.0.color: default white.perspective: default true (set false for screen-space lines).
vector_arrow
vector: EQL expression yielding a 3-component vector (required).origin: EQL for arrow base;world_posor 3-tuple (optional).scale: numeric multiplier (default 1.0).normalize:#true/#false; normalize before scaling (default false).body_frame/in_body_frame: apply origin rotation to the vector (default false).color: arrow color (default white).name: label text; used for legend/overlay (optional).show_name: show/hide overlay label (default true).arrow_thickness: numeric thickness multiplier with 3-decimal precision (default0.1).label_position: proportionately 0.0–1.0 along the arrow (0=base, 1=tip) for label anchor, or absolutely by specifying a number in a string with an 'm' suffix, .e.g., "0.3m" for 0.3 meters from origin (default "0.1m").
Schema at a glance
Legend: parentheses group alternatives; | means “or”; square brackets [...] are optional; curly braces {...} repeat; * is zero-or-more, + is one-or-more; angle brackets <...> mark positional args.
schematic =
( theme
| window
| panel
| object_3d
| line_3d
| vector_arrow
)*
theme = "theme"
[mode=dark|light]
[scheme=string]
window = "window"
[path|file|name=string]
[title=string]
[screen=int]
[rect x y w h]
panel =
viewport
| graph
| component_monitor
| action_pane
| query_table
| query_plot
| data_overview
| schematic_tree
| video_stream
| dashboard
| split
| tabs
split = ("hsplit" | "vsplit")
[active=bool]
[name=string]
{ panel [share=float] }+
tabs = "tabs" { panel }+
viewport = "viewport"
[fov=float]
[near=float]
[far=float]
[aspect=float]
[active=bool]
[show_grid=bool]
[show_arrows=bool]
[create_frustum=bool]
[show_frustums=bool]
[frustums_color=color_name_or_tuple]
[frustums_thickness=float]
[hdr=bool]
[name=string]
[pos=eql]
[look_at=eql]
{ vector_arrow }
graph = "graph" eql
[name=string]
[type=line|point|bar]
[lock=bool]
[auto_y_range=bool]
[y_min=float]
[y_max=float]
{ color }*
component_monitor = "component_monitor"
[name=string]
[component_name=string]
action_pane = "action_pane"
[name=string]
[lua=string]
query_table = "query_table"
[name=string]
[query=string]
[type=eql|sql]
query_plot = "query_plot"
[name=string]
[query=string]
[refresh_interval=ms]
[auto_refresh=bool]
[color]
[type=eql|sql]
[mode=timeseries|xy]
[x_label=string]
[y_label=string]
data_overview = "data_overview"
[name=string]
schematic_tree = "schematic_tree"
[name=string]
video_stream = "video_stream" <msg_name>
[name=string]
dashboard = "dashboard" { dashboard_node }+
object_3d = "object_3d"
<eql>
{ glb { animate }*
| sphere
| box
| cylinder
| plane
| ellipsoid
}
[emissivity=float]
{ [visibility_range] }
[icon]
animate = "animate"
joint=string
rotation_vector=eql
icon = "icon"
(builtin=string | path=string)
[size=float]
{ [visibility_range] [color] }
visibility_range = "visibility_range"
[min=float]
[max=float]
[fade_distance=float]
line_3d = "line_3d"
<eql>
[line_width=float]
[color]
[perspective=bool]
vector_arrow = "vector_arrow"
<vector-eql>
[origin=eql]
[scale=float]
[normalize=bool]
[body_frame|in_body_frame=bool]
[color]
[name=string]
[show_name=bool]
[arrow_thickness=float]
[label_position=0..1]
color = "color"
( r g b [a]
| name [alpha]
)
OBS Studio Integration
The video_stream panel can display live video from OBS Studio. There are two integration paths:
SRT Receiver (Recommended)
OBS Studio has built-in SRT (Secure Reliable Transport) support. A GStreamer receiver pipeline on the Elodin server demuxes the MPEG-TS stream and forwards H.264 frames to Elodin DB.
OBS configuration: Settings -> Stream -> Custom -> srt://ELODIN_IP:9000?mode=caller
Elodin-side receiver pipeline:
A convenience script and full example are provided in examples/video-stream/.
obs-gstreamer Direct Pipeline (Alternative)
If the obs-gstreamer plugin is installed on the OBS machine, H.264 can be piped directly into elodinsink without an intermediate process.
OBS output pipeline (configured in obs-gstreamer settings):
video. ! h264parse config-interval=-1 ! elodinsink db-address=ELODIN_IP:2240 msg-name="obs-camera" audio. ! fakesink
This requires both obs-gstreamer and elodinsink to be installed on the OBS machine.
Recommended OBS Encoder Settings
| Setting | Value |
|---|---|
| Encoder | x264 (Software) or NVENC (Hardware) |
| Rate Control | CBR |
| Bitrate | 2500–6000 kbps |
| Keyframe Interval | 2 seconds |
| Profile | Baseline or Main (High also works) |
| Tune | zerolatency |
Important: Use H.264, not H.265/HEVC. Elodin's video decoder only supports H.264.
Examples
Minimal viewport + graph:
theme mode="light" scheme="matrix"
viewport name="Main"
fov=45.0
active=#true
show_grid=#true
pos="drone.world_pos"
look_at="(0, 0, 0)"
graph "drone.altitude"
name="Altitude"
auto_y_range=#true
Video stream panel (e.g. from OBS Studio):
video_stream "obs-camera" name="OBS Camera"
Viewport + video stream side by side:
hsplit {
viewport name="3D View" show_grid=#true share=0.6
video_stream "obs-camera" name="OBS Camera" share=0.4
}
Vector arrow with custom color and label:
vector_arrow
"drone.vel_x,drone.vel_y,drone.vel_z"
origin="drone.world_pos"
scale=1.5
name="Velocity"
normalize=#true
body_frame=#true
arrow_thickness=1.500
label_position=0.9 {
color 64 128 255
}
Rigged GLB model with animated joints:
The rotation_vector is an angle-axis: the direction encodes the axis, and the
magnitude encodes the angle in degrees.
object_3d rocket.world_pos {
glb path="rocket.glb"
animate joint="Root.Fin_0" rotation_vector="(0, rocket.fin_deflect[0], 0)"
animate joint="Root.Fin_1" rotation_vector="(0, rocket.fin_deflect[1], 0)"
animate joint="Root.Fin_2" rotation_vector="(0, rocket.fin_deflect[2], 0)"
animate joint="Root.Fin_3" rotation_vector="(0, rocket.fin_deflect[3], 0)"
}
Distance icon with independent visibility ranges:
object_3d satellite.world_pos {
glb path="satellite.glb" {
visibility_range max=500.0
}
icon builtin="satellite_alt" {
visibility_range min=500.0
color 76 175 80
}
}
Icon with fade-in (both mesh and icon visible by default, icon fades in over 50 units starting at distance 200):
object_3d drone.world_pos {
glb path="drone.glb"
icon path="drone-icon.png" size=48 {
visibility_range min=200.0 fade_distance=50.0
color 0 188 212
}
}
Overlapping ranges (both mesh and icon visible between 400 and 600 units):
object_3d rocket.world_pos {
glb path="rocket.glb" {
visibility_range max=600.0
}
icon builtin="rocket_launch" {
visibility_range min=400.0
color 244 67 54
}
}
Browse all available built-in icon names at Material Icons (use the snake_case version of the icon name, e.g. "Satellite Alt" becomes satellite_alt).