Make a live wallpaper

A scene behind your canvas whose creatures are your agents: build one, ship it in a package, publish it.

Make a scene of your own that runs behind your canvas, with one creature for each of your agents, as Shoal's sharks are. You or your coding agent can take it from an idea to HiveHub with the hive command.

Paste this into your coding agent to build a live wallpaper, use it as your background and publish it.

Show the prompt
Build a Hivemind live wallpaper (a scene drawn behind the canvas in a sandboxed frame, one creature for each of my agents) and, if I want, publish it on HiveHub.

1. Ask me what the scene is: its world, what an agent's creature is, and what a creature does while its agent works, is ready, needs me, and rests. Ask its name (lowercase letters, digits and dashes).
2. Start it: `hive plugins new wallpaper <name>`. It writes a package of that name holding the wallpaper in `wallpapers/<name>`, with `startup.wallpaper` set. It takes the owner from the GitHub account `gh` is signed in as. If `gh` is not signed in, ask me for my GitHub login and pass `--owner <login>`. To add the wallpaper to a package I already have, pass `--in <package>` instead.
3. Read `PROMPT.md` in full, and the two skills it copied into the package's `.claude/skills/`: hivemind-live-wallpaper (the contract) and ascii-character-animation (the craft). Write the scene in `src/main.ts` from them. The wallpaper has no network and no permissions. It is told each agent's creature (never a name), where the tiles are, the colours, whether to run, and the pointer and clicks over empty canvas.
4. Where the starter's `package.json` is, run `npm install` once, then `npm run build` after each change, then `hive plugins check <package>`. It must say ok. It names the file and line of each problem, and loads the wallpaper in a headless browser to check it says ready and drawn and names only creatures it was sent.
5. Install it: `hive plugins install <package>`. Ask me before changing my background. On a yes, run the command the install prints for it (`--json` gives it as startWallpaper), or `hive config set appearance.wallpaper.kind '"<the wallpaper's id>"'`. Its id is `@owner/package/<folder>`.
6. Done means: it draws in my theme's two colours, light and dark; its creatures keep off my tiles; the creature of an agent that needs me goes to the open side of that agent's tile; it draws nothing on its own while held and one settled frame while still; and `README.md` says what it is.
7. Publish only if I say so. Commit the package with the wallpaper's `dist/`, push it to GitHub, and run `hivehub publish <package>`, as https://hivemind.griiken.com/guide/publish-to-hivehub.md says. If I must sign in, ask me to open the link and type the code myself.

At the end, tell me the wallpaper's id, the command others use to install it (`hive plugins install @owner/package`), and anything you could not check.

REFERENCE https://hivemind.griiken.com/guide/live-wallpapers.md

What a live wallpaper is

A live wallpaper is a small web page that the app runs behind the canvas, in a sandboxed frame, as it runs a view. Shoal, Koi Pond, Jellyfish and Fireflies are the ones that come with the app (how to pick one). Yours ships in a package, and its id is @owner/package/<folder>.

What it is told is less than a view is:

  • Each agent's creature: an id that means nothing outside the scene, the program (claude, codex …), a kind from 0 to 1 that is the same for every agent of one program, a size, and what the agent is doing: working, ready, needs-you or resting.
  • Where the tiles are on the screen, and whether they are see-through.
  • The colours to draw in, and whether to run, keep its last frame or show one settled frame.
  • The pointer and clicks over empty canvas, and a turn an agent finished well.

It is never told an agent's name, a folder, a command, a tile's own id or anything a terminal shows. It loads and fetches nothing from the network, has no permissions, never has the keyboard, and cannot change the workspace. The two things it says back are that its first frame is up, and which creature is under the pointer. While the pointer is over empty canvas, the app shows that agent's name and state beside the pointer, and a click there goes to the agent's tile.

Like a view, its frame can still open a WebRTC connection, which no page policy in Chromium stops, and so could send what it is told elsewhere. Install live wallpapers, like views, from people you trust.

It is not drawn behind a view you installed, which lays its tiles out itself, nor on the phone page.

Before you start

  • Hivemind 2026.10.8 or newer, open on a project with a few agents.
  • Node 22 or newer, for npm.
  • You know a little TypeScript or JavaScript, or your coding agent does.

Start it

hive plugins new wallpaper pond

It writes the package pond/. Its hivemind-package.json names the wallpaper in wallpapers/pond and offers it as the background once it is installed (startup.wallpaper). The wallpaper's folder holds its hivemind-wallpaper.json and a working scene in src/main.ts. A PROMPT.md and two skills in the package's .claude/skills/, one for the contract and one for drawing in characters, are there for your coding agent.

The owner is the GitHub login gh is signed in as, or the one you pass with --owner. To add a wallpaper to a package you have, run hive plugins new wallpaper pond --in kit.

Check: hivemind-package.json has "startup": { "wallpaper": "@<your login>/pond/pond" }.

Write the scene

The starter draws a working scene. The smallest one that runs on the app's clock looks like this:

src/main.ts
import { connectWallpaper } from "@hivemind/view-sdk";

const wp = await connectWallpaper();
const canvas = document.body.appendChild(document.createElement("canvas"));
Object.assign(canvas.style, { position: "fixed", inset: "0" });
const g = canvas.getContext("2d")!;
const at = new Map<string, { x: number; y: number }>();

// Sharp on any screen: the canvas is the viewport times its pixel ratio.
wp.on("viewport", ({ w, h, dpr }) => {
  canvas.width = Math.round(w * dpr);
  canvas.height = Math.round(h * dpr);
  Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` });
});
// An agent that closed: its creature goes.
wp.on("creatures", (cs) => {
  for (const id of at.keys()) if (!cs.some((c) => c.id === id)) at.delete(id);
});
// Which creature is at a point: the app shows its agent's name, and a click opens its tile.
wp.hitTest((x, y) => {
  for (const [id, p] of at) if (Math.hypot(p.x - x, p.y - y) < 12) return id;
  return null;
});

wp.animate({
  step(dt) {
    const { w, h } = wp.viewport;
    for (const c of wp.creatures) {
      const p = at.get(c.id) ?? { x: Math.random() * w, y: Math.random() * h };
      p.x = (p.x + (c.state === "working" ? 60 : 15) * dt) % w;
      at.set(c.id, p);
    }
  },
  draw() {
    const { w, h, dpr } = wp.viewport;
    const { page, text, attention, mono } = wp.look;
    g.setTransform(dpr, 0, 0, dpr, 0, 0);
    g.fillStyle = page;
    g.fillRect(0, 0, w, h);
    g.font = `16px ${mono}, monospace`;
    for (const c of wp.creatures) {
      const p = at.get(c.id);
      if (!p) continue;
      g.fillStyle = c.state === "needs-you" ? attention : text;
      g.fillText("@", p.x, p.y);
    }
  },
});

This one does not yet keep off the tiles or go to its agent's tile; the sections below say how. To have your coding agent write it, put your idea at the top of PROMPT.md and hand it the file.

Build and check it

Where the starter's package.json is, run npm install once. Then, after each change:

npm run build              # writes dist/main.js, the entry its manifest names
hive plugins check pond

The check reads the package and every member as the install and HiveHub do, with the file and line of each problem. It also loads the wallpaper in a headless browser against a pretend app, and checks that it says it is ready, draws its first frame, and names only creatures it was sent.

Check: it ends with ok. --json gives the same report to an agent.

Install it and use it

hive plugins install ./pond

A package of live wallpapers, views, widgets and themes holds no code that runs outside a sandbox, so it arrives switched on. Because the package names its wallpaper in startup.wallpaper, the app offers it as your background once, and never switches without you. The install prints the command that switches to it; --json gives it as startWallpaper. To switch yourself:

hive config set appearance.wallpaper.kind '"@<your login>/pond/pond"'

Or open Settings → Appearance and pick it under Background, after the built-in scenes. Install again after each change.

Check: your agents swim behind the canvas. Point at one and its agent's name shows.

Publish it

Commit the package with the wallpaper's dist/, push it to GitHub, and run hivehub publish pond. A package publishes as one listing: see Publish a plugin. HiveHub lists a package that holds a live wallpaper as needing Hivemind 2026.10.8 or newer.

What it is told, and what it says

The app and the wallpaper talk over one message port. Every message is a JSON object with a type, checked on both sides. @hivemind/view-sdk speaks it for you: connectWallpaper() waits for the app and gives you the values and their changes as wp.on(…).

The app sendsHoldsWhen
helloeverything below, at onceonce, after it says ready
viewport{ w, h, dpr }: its size in CSS pixels, and the screen's pixel ratiothe window is resized or moves to another screen
look{ page, text, attention, dark, colour, mono }: the theme's page, text and needs-you colours as #rrggbb, whether the theme is dark, whether to draw in its own colours, and the terminals' font as a CSS font listthe look changes
moderun, hold or stillsee the clock
creaturesup to 8 of { id, agent, kind, size, state }an agent comes, goes or changes state
tiles{ tiles, seeThrough }: each tile, and each panel that is not glass, as { x0, y0, x1, y1, id? } in CSS pixels; id is the creature's id on its agent's tileonce a second while it runs, and at most once a frame while the canvas moves
done{ id }: that creature's agent finished a turn wella turn ends well
pointer{ x, y, over }: the pointer over empty canvas, or gone from itat most once a frame while it moves there
click{ x, y, id }: a click on empty canvas, and the creature it opened, or null for open watera click
It sendsHoldsThe SDK sends it
ready{ protocol: 1 }in connectWallpaper()
drawnnothing: the first frame is up, and the app shows the layerafter the first frame of animate, or with wp.drawn()
hover{ id }, the creature under the pointer, or nullwhen what hitTest answers changes

A creature's size is the same for its agent every time, from about 0.78 to 1. Pick what kind of creature it is from kind, so all of one program's agents look alike.

The clock

wp.animate({ step, draw, settle }) runs your scene on the clock the built-in scenes use:

  • step(dt) moves the scene on by 1/60 of a second, as many times as the time that passed needs (at most four a frame; a busy computer drops the rest). The scene moves at the same speed whatever the frame rate.
  • draw(dt) draws it as it is, at most 30 times a second. Characters read smooth at 30, and it costs half of 60. animate(scene, { fps: 60 }) asks for more.
  • settle(), if you give it, puts everything where it would be after a while. Without it, the clock runs 360 steps.

The mode decides what runs:

ModeWhenWhat the clock does
runthe window is in usesteps and draws
holdthe window is hidden, unfocused, idle or coverednothing. It draws one frame again only when something it shows changes: the size, the look, the creatures or the tiles
stillthe person turned off Animate the background, or the computer asks for reduced motionone settled frame: settle() and one draw. Again only when something it shows changes

Its first frame is drawn at once, whatever the mode. Never start a timer or a requestAnimationFrame loop of your own: while held, a wallpaper that keeps drawing spends the battery on a window nobody sees.

Keep off the tiles

Your agents' tiles sit over the scene, and their text must stay readable. Use tiles:

  • Pick where a creature goes in the open water between the tiles, and a way there that does not pass under one.
  • Turn a creature away before it goes under a tile. One a tile was put down over goes out by the nearest side, quickly.
  • Draw nothing of a creature under a tile. When seeThrough is true the tiles are frosted glass, and a creature under one may show through, soft and faint.

When an agent needs you, its creature goes to that agent's tile: the tile in tiles whose id is the creature's. It holds by the open side, the side with more room clear of the other tiles, else the other side, else above or below. When the tile is not on the screen, the creature stays in the open water.

Hover and click

wp.hitTest((x, y) => id) answers which creature is at a point of the viewport, or null. While the pointer is over empty canvas, the app shows that creature's agent's name and state beside it, and a click there goes to the agent's tile. The wallpaper never learns the name, and the app takes hover only for a creature it sent.

Every click on empty canvas comes as click. Its id is the creature the app opened, or null for open water: answer a click on open water in your scene, with a ripple or a scatter.

The built-in scenes answer the pointer the same way.

Colours

  • Your theme's colours. By default draw in page and text and the steps between them, so the scene follows every theme. dark says which way round: light creatures on a dark page, or ink on paper on a light one. Keep attention, the theme's colour for an agent that needs you, for that alone.
  • Its own colours. With "colour": true in its manifest, Settings shows the Colour switch for it, and look.colour says when the person turned it on. Then draw in your scene's own colours: one set for a dark theme and one for a light one.
  • Characters. mono is the terminals' font, as a CSS font list. It is the person's choice and may not be on this computer, so end it with a fallback: `${mono}, monospace`.
  • Panel text over it. With glass on, the Layers panel and the toolbars are frosted glass, and their text sits over your scene. Keep the scene dark enough on a dark theme, and light enough on a light one, that the text reads over every part of it. Test it: the built-in scenes keep the Layers panel's text at 7:1 and its secondary text at 4.5:1 over every point of their water.

hivemind-wallpaper.json

FieldNeededTakes
protocolyes1
nameyesup to 60 characters: the name in Settings → Appearance
entryyesan .html page or a .js script inside the folder, usually dist/main.js. A script is put in a page of the app's own
descriptionnoone sentence, up to 200 characters, shown under its name
colournotrue when it draws in colours of its own with Colour on
previewnoa .png, .jpg or .webp inside the folder, up to 512 KB, shown in the picker
{
  "protocol": 1,
  "name": "Pond",
  "entry": "dist/main.js",
  "description": "Koi under lily pads, one for each agent.",
  "colour": true,
  "preview": "preview.png"
}

The folder's name is the last part of its id: lowercase letters, digits and single dashes.

Ship it in a package

A live wallpaper always comes in a package, which names it in hivemind-package.json:

"wallpapers": [{ "path": "wallpapers/pond" }],
"startup": { "wallpaper": "@you/pond/pond" }

startup.wallpaper names one of the package's own wallpapers. Once the package is installed and on, the app offers it as the background, once per package, as startup.view offers a view. The person chooses; nothing switches without them.

A package may hold a live wallpaper alone, or beside a view and a theme that go with it. Archipelago (@dip497/archipelago on HiveHub) ships its view with its widgets and a theme, and offers the view with startup.view; a wallpaper is offered the same way. Give startup both a view and a wallpaper, and the app offers both. The rest of what a package holds, and how to update and roll it back: Packages.

What turns it off

The app turns a live wallpaper off for the session, shows a notice, and draws the plain background instead, when it:

  • sends 8 messages that are malformed, or that name a creature the app did not send;
  • sends more than 120 messages a second;
  • asks for a newer protocol than this Hivemind speaks;
  • takes the keyboard (focuses anything in its frame);
  • uses 60% of a CPU core for six seconds.

The scene runs in a process of its own, so it never slows your terminals, but a busy one is still turned off. Measure what a frame costs: the hivemind-live-wallpaper skill in your package's .claude/skills/ says how.

Limits

WhatMost
Creatures8. With more agents, the ones that need you come first, then the ones at work
Tiles in one tiles message256
Live wallpapers in a package8
A preview picture512 KB
Frames a second30, or what fps asks, up to 60
Waiting for the appconnectWallpaper() gives up after 10 seconds

Commands

hive plugins new wallpaper <name>              # a package holding a working wallpaper, with PROMPT.md
hive plugins new wallpaper <name> --in <pkg>   # one more in a package you have
hive plugins check <package>                   # the manifests, and the wallpaper loaded once
hive plugins install <package>|@owner/package
hive config set appearance.wallpaper.kind '"@owner/package/name"'
hivehub publish <package>

The hive plugins commands are in the CLI reference.

Last validated for Hivemind 2026.10.3.

Edit on GitHub

On this page