Layouts
Build a custom radar detector skin from HTML and CSS using the data-hud-* binding grammar.
A layout is a skin: a folder under layouts/ holding a module.json manifest, a
ui.html entry point, and its images. Folders are auto-discovered on server start and
appear in the HUD Layout selector in /tdetector.
Every element that changes carries a data-hud-* attribute and tLib's HudBinder writes
state into it — the grammar tRadar's, tELS's and tRadio's skins also use. A skin decides
only how it looks; the timing is shared.
Bundled layouts
| Folder | Size | What it is |
|---|---|---|
RDX-8000 | 480 × 197 | Compact windshield unit, the default. Red-outlined speed box, one large block arrow, band / frequency / a skewed green-to-red segment meter |
RDX-4000 | 480 × 211 | Wide strip. Cyan chamfered speed tag, a cyan rule above a status row, and a stepped meter whose bars rise in height as well as colour |
ARC-360 | 480 × 189 | 360-degree unit. Blue speed block, orange band tile, frequency over a red stepped meter. Direction is eight physical arrow lenses, not on-screen arrows |
All three on the same alert — 38 MPH, Ka on 34.700 GHz, six of eight bars, source ahead:
All three are dot-matrix OLEDs: a .px overlay lays a black mesh on a 2–3px pitch, and
each lit element carries a text-shadow or box-shadow halo. Keep both — and
nothing below ~12px is legible.
Folder structure
A layout folder holds module.json, ui.html and its images; layouts/sounds/ holds
the shared .wav files, which every layout uses. Image src attributes are rewritten to
absolute before injection, so use relative paths. Sounds are not a layout's concern —
they are mapped in /tdetector → Sounds.
The binding grammar
| Attribute | Effect |
|---|---|
data-hud-text="key" | textContent becomes state[key] |
data-hud-show="key" | Visible while state[key] is truthy |
data-hud-hide="key" | Hidden while state[key] is truthy |
data-hud-eq="key:value" | Gains .active while state[key] === value |
data-hud-led="key" | Gains .active while state[key] is truthy |
data-hud-html="key" | innerHTML becomes state[key] |
data-hud-attr="key:attr" | Sets attr to state[key]; removes it when null, "" or false |
data-hud-led-color="key" | backgroundColor from an { r, g, b, a } value |
data-hud-btn="action" | Click sends action to Lua — mute, dim or mark |
Reach for the first six: no tDetector state key is an { r, g, b, a } colour, and
data-hud-text covers what data-hud-html would. In play the HUD takes no pointer
events, so data-hud-btn regions are reachable only in the HUD editor and the
marketplace preview.
State keys
The keys the driver emits.
| Key | Type | Description |
|---|---|---|
powered | boolean | Detector on and the player is in a vehicle |
boot | string | "logo", "selftest", "sweep", "gps", or "" once complete |
idle | boolean | Powered, booted, nothing detected |
alert | boolean | A radar source is being tracked |
bandKey | string | "ka", "k", "x", "laser", or "" |
bandLabel | string | The same band as printable letters ("Ka") |
freqText | string | Frequency in GHz, e.g. "34.712" |
bar1 … bar8 | boolean | Signal strength, lit cumulatively |
speedText | string | Rounded speed |
unitLabel | string | "MPH" or "KM/H" |
arrowFront arrowRear arrowLeft arrowRight | boolean | Direction; flashes in time with the blip |
muted | boolean | Alerts silenced |
dim | boolean | Display turned down, across the whole HUD |
multiThreat | boolean | More than one source is audible |
bogeyText | string | How many, e.g. "3". Empty while idle. |
modeKey | string | "constant", "instant", or "" — for data-hud-eq |
modeLabel | string | "I/O" for instant-on, empty for constant-on |
Create a layout
Create the folder
layouts/MyLayout/ — the folder name is what players see in the layout selector.
Add module.json
{
"schema": 1,
"kind": "layout",
"id": "MyLayout",
"name": "My Layout",
"version": "1.0.0",
"author": "You",
"license": "UNLICENSED"
}id must equal the folder name exactly, version must look like 1.2.3, and kind
must be "layout". Each is rejected outright rather than warned about, and sounds and
themes are reserved.
Add ui.html
One root element with fixed pixel bounds, its own <style> block, and the markup.
Declare data-radio-width / data-radio-height on the root: the marketplace preview
sizes its canvas from them, and the HUD editor needs them to place the skin.
Add images, then restart
restart tDetectorGeometry a skin must provide
The HUD editor measures the injected container's unscaled, in-flow bounds:
RDX-8000 is 480 × 197, RDX-4000 480 × 211, ARC-360 480 × 189.
A skin must keep a stable, intrinsic, in-flow footprint, not draw outside it, and not size itself in viewport units.
Minimal example
<div class="ui-root det" data-radio-width="200" data-radio-height="80"
style="position:relative;width:200px;height:80px;">
<style>
.det { background: #111; color: #0f0; font-family: monospace; }
.det .row { position: absolute; left: 10px; }
/* eq elements toggle a class, so they may start hidden */
.det .boot { position: absolute; inset: 0; display: none; }
.det .boot.active { display: block; }
</style>
<div class="boot" data-hud-eq="boot:logo">MY DETECTOR</div>
<div class="row scan" data-hud-show="idle">Scanning…</div>
<div class="row band" data-hud-show="alert" data-hud-text="bandLabel"></div>
<div class="row freq" data-hud-show="alert" data-hud-text="freqText"></div>
<div class="row speed" data-hud-show="powered" data-hud-text="speedText"></div>
<div data-hud-btn="mute" role="button" tabindex="0"
style="position:absolute;right:6px;bottom:6px;width:30px;height:20px;"></div>
</div>UI Studio
Build and publish custom layouts with the UI Studio — drafts, design vs HTML mode, lint rules, bundle limits, forking and publishing are all in the Studio itself.
Next steps
Radar Detector
Still stuck? Support can answer it directly.
Was this page helpful?
TIMMYG Studios