Node packs
A node pack is a set of nodes you can add to Studio's graphs: new steps for visual scripts, new pieces for materials. Studio's own nodes come as two packs. You can make your own, share them as a .motionpack file, and add ones other people share.
What a pack is
A pack is a folder. Each node is one TypeScript file in it, holding the node's title, where it goes in the node search, its pins, its help, its tests and what it does.
my.lights/
pack.json its id, name, version, about and author
nodes/ the nodes, one .ts file each, in folders as you like
glsl/ GLSL functions its material nodes call, one a file
kit.d.ts the types the node files are written with
tsconfig.json so your code editor checks the node files
Studio's own two packs name their script nodes for what they do in a scene (On Scene Start, Get Scene Time, Set Layer Setting), and their material nodes as node-based shader tools such as Unity's Shader Graph, Blender and Godot name them (the few those tools haven't got are named for what they do). Each page in the script and material reference lists the node under that name. Your own nodes show the title, category and pin names you give them.
A pack's id is words of lowercase letters, digits and hyphens with dots between, such as acme.lights, 64 characters at most. Each node's id starts with the pack's (acme.lights.pulse). Graphs refer to nodes by their id, so once people use a node, its id shouldn't change.
The Node packs panel
In Studio, find Node packs in the command palette (Ctrl+K), or open Assets › Library › Node packs. The panel lists the packs you've added, each with its name, id and version, its nodes, and any problems in its files (the file, the line and what's wrong).
- Reload reads the packs again, after you've changed their files. Their nodes are in the node search straight away.
- Add a .motionpack… adds a pack someone shared. If you already have a pack with the same id, the new one replaces it.
- On each pack: Test runs its tests, the folder button opens its folder, Share… saves it as a
.motionpack, and the trash button takes it away.
Packs live in ~/.local/share/motion/packs on Linux and %LOCALAPPDATA%\motion\packs on Windows, a folder each, named by the pack's id.
Using a pack's nodes
Once a pack is added, its nodes are in the node search of the visual script and the material editor, under the category each node names. Script nodes go in visual scripts, material nodes in materials.
A scene made with a pack's nodes carries their code in its own script and shaders. So the wallpaper plays anywhere, on a computer that has never seen the pack. To change those nodes in Studio, though, you need the pack added.
Making your own
- In the Node packs panel, type a name under Pack name and click New pack. Studio makes the folder, with a node to start from, and opens it.
- Open the folder in any code editor. The
kit.d.tsandtsconfig.jsonin it let the editor complete and check the node files. - Change the starting node, or add more: type a title under Node title, pick its kind, and click New node. Studio writes a file to start from in
nodes/. - Click Reload in Studio. Problems show on the pack's card, with their lines; the nodes are in the search.
- Click Test to run the tests.
Studio makes a new pack's id from its name, starting my. (a pack called "Lights" is my.lights). You can change the id and the rest in pack.json.
Studio reads a node file without running it. Write the pins and their defaults out as they are, not worked out by other code.
A node
import { pure, num } from "@motion/nodes";
export default pure({
id: "my.lights.pulse",
title: "Pulse",
category: "Lights|Timing",
about: "A brightness that rises and falls once a period.",
ins: { time: num(0, "Time", { unit: "s" }), period: num(2, "Period") },
out: num("Brightness"),
value({ time, period }) {
return 0.5 - 0.5 * Math.cos((time / period) * Math.PI * 2);
},
tests: [{ in: { time: 1, period: 2 }, out: 1 }],
});
category says where it goes in the node search: "Lights|Timing" is Timing under Lights. about is the node's help, shown on hover. keywords adds words the search finds it by, and order places it within its category.
The four kinds
- pure: outputs worked out from inputs by
value, with no exec pins. One output isout; several areouts, andvaluegives back an object of them. - action: an exec pin in and out. Its
run(ctx, inputs)does something to the scene, and may take its time:await ctx.wait(seconds). It can have exec outputs of its own, which it fires withctx.fire("done"). Its then goes on whenrunis done. - event: started by the scene.
onis one of"start","tick","click","pointer","beat","props","pause"or"resume", and its outputs are what the scene's event gives, in order. - material: GLSL for the material editor.
glslputs its inputs' GLSL into a template;usesnames the pack's GLSL functions it calls, fromglsl/.
An action's ctx can set, move and read layers' settings (ctx.set, ctx.animate, ctx.get), wait, fire its outputs, keep state of its own while the scene runs (ctx.state()), log, and reach the scene's motes through ctx.stage.
import { action, layer, num, exec } from "@motion/nodes";
export default action({
id: "my.lights.fadeOut",
title: "Fade out",
category: "Lights|Actions",
about: "Fades a layer out, then goes on with Done.",
ins: {
target: layer("Target"),
seconds: num(0.5, "Seconds", { min: 0, unit: "s" }),
},
outs: { done: exec("Done") },
async run(ctx, { target, seconds }) {
ctx.animate(target, "opacity", 0, seconds);
await ctx.wait(seconds);
ctx.fire("done");
},
});
Pins
Each pin is made by a builder, which takes its default, its name and options (min, max, unit, and about for its hover help):
- for scripts:
num,int,text,bool,colour,vec2,vec3,layer,mote,moteClass,choice,list,anyandexec - for materials:
f1tof4(one to four numbers) andgen(whichever is widest of what's wired in)
The full list, with what each takes, is in kit.d.ts in your pack's folder.
Tests
tests lists inputs and what the node should give. Inputs you leave out take their defaults; tol says how near a number has to be. Test in the panel runs the tests of pure nodes. An action's or event's tests need a scene, and a material's need drawing, so the panel marks those as skipped.
Sharing
Share… on a pack's card saves it as a .motionpack file: the folder, zipped, with pack.json at the top. Send it to someone, and they add it with Add a .motionpack…. To update a pack you've shared, raise its version in pack.json and share it again; adding it replaces the old one.
Remember that people only need your pack to edit the nodes. The wallpapers made with it play without it.
Last changed 2 November 2026. Something wrong here? Tell us.