Motion
Get Motion

DocsStudio

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.

5 min readMotion

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).

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

  1. 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.
  2. Open the folder in any code editor. The kit.d.ts and tsconfig.json in it let the editor complete and check the node files.
  3. 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/.
  4. Click Reload in Studio. Problems show on the pack's card, with their lines; the nodes are in the search.
  5. 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

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):

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.

Ask about this page on the forumAsk a question in Help

Last changed 2 November 2026. Something wrong here? Tell us.