Writing LMS Scripts
LMS Script is a small, friendly language for generating live LED-matrix animations. You write a bit of setup and a bit of per-frame drawing, and LMS runs it straight onto your wall. It's the same format as classic Jinx! scripts (.jns), so scripts from the wider community work too.
Quick start
Every script has two parts: :init (runs once) and :render (runs every frame). Here's a complete script that scatters coloured sparkles:
:init # runs ONCE when the effect starts w = matrix_x h = matrix_y end :render # runs EVERY frame — draw into the matrix clear x = rnd(w - 1) y = rnd(h - 1) pset x, y, autocolor_red, autocolor_green, autocolor_blue end
Open the Edit script button under the effect's Script dropdown to try it — you get a live preview and instant error checking as you type.
Importing Jinx scripts
LMS Script is Jinx!Script — the same language and the same .jns file format used by the classic Jinx! software. That means the huge library of scripts people have written and shared over the years runs straight in LMS, no conversion needed.
To import a script
In the effect's Script dropdown, open the My scripts section at the bottom and choose:
+ Load .jns file… | Pick any .jns file from your computer. It's added to your library, appears in the list, and starts playing. |
+ New script… | Start a blank script from scratch in the editor. |
⚙ Manage my scripts… | Rename, edit or delete the scripts you've added. |
Imported scripts are saved inside your project (the .lmc file), so they travel with it — share a project and your scripts go along. Load as many as you like; they all stack up in the list.
.jns — there are collections on forums, GitHub and the LED-hobby community. Drop the file in via Load .jns file… and it's yours.Structure
A script is a list of plain-text lines. Blank lines and anything after a # are ignored (comments).
| Block | Runs | Use it for |
|---|---|---|
:init … end | Once, at the start | Set starting values, sizes, arrays, config settings. |
:render … end | Every frame | Draw the current frame. Runs ~30–60 times a second. |
Variables you set in :init keep their values between frames, so :render can move things along a little each time — that's how animation works.
The matrix & colour
The matrix is a grid of pixels. (0, 0) is the top-left; x goes right, y goes down. The size is always available as matrix_x (width) and matrix_y (height) — write to those instead of hard-coding a size, and your script works on any wall.
Colours are three numbers — red, green, blue, each 0–255. So 255, 0, 0 is red and 255, 255, 255 is white.
Variables & arrays
Just assign to create a variable — no declarations. Numbers can be whole or decimal.
count = 0 angle = 3.14 count = count + 1
Arrays use square brackets and grow as needed (index from 0):
red[0] = 255 red[1] = 0 x[i] = x[i] + speed[i]
config settings
Use config in :init to declare a named value with a sensible default:
:init config dot_count = 100 config speed = 2 end
It behaves like a normal variable you can read anywhere. (Per-script sliders for these are on the roadmap; for now they run at their default, and the effect's global Speed, Auto-colour speed and Brightness controls sit on top.)
Loops & conditions
Repeat a fixed number of times — for … next
for i = 0 to dot_count step 1 pset i, 0, 255, 255, 255 next
step is optional (defaults to 1) and can be negative to count down.
Repeat while true — while … wend
i = 0 while i < w pset i, i, 0, 255, 0 i = i + 1 wend
Make decisions — if … else … endif
if x > w x = 0 else if x < 0 x = w endif
Use & (and) and | (or) to combine conditions: if x > 0 & x < w. A trailing then is allowed but optional.
Subroutines — gosub
Give a block a label, jump to it with gosub, and come back with return:
:render gosub new_dot end :new_dot px = rnd(matrix_x) py = rnd(matrix_y) return
Operators & maths
| Kind | Operators |
|---|---|
| Arithmetic | + - * / % (remainder) ^ (power) |
| Compare | = (equal) <> (not equal) < > <= >= |
| Logic | & or && (and) · | or || (or) |
Note = does double duty: x = 5 assigns, but inside an if it means "equals".
Drawing commands
| Command | What it does |
|---|---|
clear | Blank the whole matrix to black. |
fade N | Dim every pixel by N (0–255). Great for trails — draw without clearing and let old pixels fade. |
pset x, y, r, g, b | Set one pixel to a colour. |
pget x, y, rVar, gVar, bVar | Read a pixel's colour into three variables (handy for reacting to what's on screen). |
line x1, y1, x2, y2, r, g, b | Draw a straight line. |
rect x1, y1, x2, y2, r, g, b, fill | Rectangle between two corners. fill = 1 solid, 0 outline. |
circle x, y, radius, r, g, b, fill | Circle at a centre. fill = 1 solid disc, 0 outline. |
text x, y, align, size, r, g, b, "string", "font", style | Draw text. align: 0 = left, 1 = centre, 2 = right (horizontal anchor at x; y is the vertical centre). size = font height in pixels. "font" = a font name (e.g. "Arial"). style: 0 = normal, 1 = bold, 2 = italic, 3 = bold+italic. The string must be a literal in quotes — Jinx! (and LMS) can't use a variable for the text content. New in this build — makes countdown / scrolling-message / bingo-style community scripts work. |
hsv2rgb hue, sat, val, rVar, gVar, bVar | Convert a colour from Hue/Saturation/Value into r,g,b variables. hue is 0–360°, sat and val are 0–255. The easy way to cycle rainbows. |
Built-in values
| Name | Meaning |
|---|---|
matrix_x, matrix_y | Matrix width and height in pixels. |
autocolor_redautocolor_greenautocolor_blue | A colour that cycles automatically over time (its speed is the effect's Auto-colour speed control). Draw with these for a built-in colour show — no maths needed. |
frame_count | How many frames have rendered — a steadily rising counter. |
audio_trigger | Live music level, 0–255 — the louder of the beat transient and overall loudness. Detect a beat by comparing to the previous frame (if audio_trigger > trigger_old). Needs an audio input running (Settings › Audio). Reads 0 when no audio is active. |
audio_level | Overall loudness, 0–255 (mean of all frequency bands). |
audio_bassaudio_midaudio_treble | Per-band energy, 0–255 — lows (~20–200 Hz), mids (~200–1500 Hz), highs (~3–12 kHz). Drive different parts of a pattern off different frequencies. |
audio_beat | Beat / onset envelope, 0–255 — spikes on kicks and decays smoothly. Cleaner for pulsing than audio_trigger. |
pi | 3.14159… — for angles and circles. |
Functions
| Function | Returns |
|---|---|
rnd(n) | A random whole number from 0 to n (inclusive). |
sin(a), cos(a), tan(a) | Trig — angle in radians (multiply degrees by pi/180). |
sqr(n) | Square root of n (also sqrt(n)). |
abs(n) | Absolute (positive) value. |
floor(n), ceil(n), round(n) | Round down / up / nearest. int(n) = floor. |
min(a, b), max(a, b) | Smaller / larger of two values. |
atan2(y, x), pow(a, b) | Angle of a vector; a to the power b. |
Worked examples
A dot bouncing around
:init x = 5 y = 5 dx = 1 dy = 1 end :render fade 40 # leave a fading trail x = x + dx y = y + dy if x <= 0 | x >= matrix_x - 1 dx = 0 - dx # bounce off the sides endif if y <= 0 | y >= matrix_y - 1 dy = 0 - dy endif pset x, y, autocolor_red, autocolor_green, autocolor_blue end
A rainbow sweep
:render for x = 0 to matrix_x - 1 hue = x * 360 / matrix_x + frame_count * 4 hsv2rgb hue, 255, 255, r, g, b for y = 0 to matrix_y - 1 pset x, y, r, g, b next next end
Tips & performance
fade N at the top of :render instead of clear to leave glowing tails behind moving shapes. Bigger N = shorter tail.autocolor_red / green / blue and the whole thing cycles colour on its own — control the speed with the effect's Auto-colour slider.:render runs every frame. A loop over every pixel that also loops again inside (width × height × more) gets very heavy on big walls and will drop the frame rate. Prefer drawing a handful of shapes/points per frame over scanning the whole grid.The live editor (Edit script) checks your script as you type and shows the exact line of any problem. Experiment freely — a built-in preset opens as an editable copy, so you can never break the originals.