Quickstart: from a blank canvas to a moving scene
We’ll begin with nothing but a background. Then we’ll draw one circle, change it while it runs, give it motion, and add a second layer. By the end, you’ll have a small scene you understand well enough to make your own.
Steps 1–9 are the first-session path: make a moving image, change it live, then save it and return to it. Steps 10–11 are optional experiments with another scene and shader effects. Thunk Machine is a visual synth, mixer, and performance instrument; this first scene introduces the code you will use to play it.
No audio file, controller, or installation is needed. You’ll write a little JavaScript, but we’ll introduce each piece as we use it.
Keep the quick-reference card nearby once you start performing.
1. Start with an empty stage
Open Thunk Machine in desktop Chrome and choose Start silent. If Thunk’s Field Guide appears, choose Maybe later while following this Quickstart. You can reopen it with Guide in the help footer.
If the app restores an earlier session, save anything you want to keep, then:
- Click Performances in the top bar.
- Select the All tab.
- Click + Performance, above the saved performance cards.
- In the New performance dialog that opens, click Use default scene.
Use default scene is in that dialog, not on the All tab. It starts a new working performance with a black stage without deleting previously saved performances.
The editor appears on the stage by default. Close the Performances drawer if
it is covering your work. If you hid the editor, press Esc and then E to
show it.
Click inside the editable code, press Cmd+A on Mac or Ctrl+A on Windows/Linux, and paste this complete program. This replaces the starter patches and scene, including their headings; the read-only Controls and Modulations at the top remain. You stay in the same editor.
// %% scene scene
const scene = [
() => background(12, 14, 20),
];
scene.activate();
Press Cmd+Shift+Enter on Mac or Ctrl+Shift+Enter on Windows/Linux to run the program and activate your scene. You should see a dark blue-black stage. That is the first result: a scene that draws a background.
If you accidentally replace or delete the wrong code, press Cmd/Ctrl+Z to undo. Cmd/Ctrl+Shift+Z redoes an edit. Undo restores the source; run it again when you want that restored version to become the running picture.
The three numbers in background(12, 14, 20) are red, green, and blue, each from
0 to 255. Change them to background(35, 20, 45) and run again. The stage turns
dark purple. Then return to background(12, 14, 20).
A scene is a list of things to draw, in order. The square brackets hold that
list. Right now it has one entry: a small function that paints the background.
scene.activate() makes this the active scene; Thunk Machine keeps drawing it for you.
const scene gives that list a name. Keep the punctuation and // %% heading
as shown while changing the numbers. You can use the manual’s
Reading the code section when you want a closer
explanation of the syntax.
2. Put one circle on the stage
We'll give the circle's drawing code its own named section, called a cell. Its heading lets Thunk Machine find that code so you can update the circle while the scene keeps playing.
Click the small + between code cells, enter dot, and click Create.
This creates a cell headed // %% patch dot. Select its heading and placeholder
code, and replace them with:
// %% patch dot
function dot() {
noStroke();
fill(255, 87, 43);
circle(width / 2, height / 2, 120);
}
With your cursor inside the dot cell, press Cmd/Ctrl+Enter to run it.
Then add dot, after the background
entry in the existing scene array. Your scene section should now look like this;
do not append a second scene:
// %% scene scene
const scene = [
() => background(12, 14, 20),
dot,
];
scene.activate();
Run the program again with Cmd/Ctrl+Shift+Enter. You should see one orange circle in the middle of the stage.
The dot function is our first patch: a named piece of drawing code.
Inside that function, the drawing code is standard p5.js. If you have used p5.js before, these are the same commands and canvas dimensions you already know:
noStroke()removes the outline.fill(255, 87, 43)sets the color of the shape.circle(x, y, diameter)draws a circle at a position and size.width / 2andheight / 2put it halfway across and halfway down the stage.
Thunk Machine provides the patch-and-scene structure and lets you update that code while it runs. p5.js supplies the drawing tools.
The scene clears the stage first, then draws dot. Notice that the list contains
dot, not dot(): we give Thunk Machine the function so it can call it each frame.
Try one change: replace 120 with 200. Run again. The circle gets larger.
Set it back to 120 before continuing.
3. Change just the patch
Now change the circle's color while the scene keeps running.
Inside dot, change the fill to:
fill(90, 210, 230);
Leave your cursor anywhere inside the dot cell and press Cmd+Enter or
Ctrl+Enter, without Shift. The circle turns blue. You updated the patch while
the scene stayed active.
For the rest of this tutorial:
- Cmd/Ctrl+Enter runs the cell containing your cursor.
- Cmd/Ctrl+Shift+Enter runs all the code in the editor.
Typing alone does not change the running picture. Running applies your edit. If an edit has an error, the last working picture keeps running. Correct the code and run the cell again. For a quick repair:
- Compare the lines you changed with the example. Check matching brackets, parentheses, and quotes, and keep commas between entries in the scene list.
- Undo the last edit with Cmd/Ctrl+Z, then run the cell again. Undo changes the text; running applies it.
- If the circle is missing, check that
dot,is in the scene and run the scene cell. If only the color or size changed, run thedotcell. - Include a section’s heading exactly once when replacing a complete cell. The complete scene below is a known working replacement.
Colors can also use a hex string, such as fill("#ff0000") for red. Keep both
the quotes and the leading #; "ff0000" is not a valid hex color. The RGB
numbers used in this tutorial each range from 0 to 255.
For more help, open Settings → Diagnostics and see the manual’s source-changed, image-unchanged guidance.
Change the fill back to fill(255, 87, 43) and run the dot cell.
4. Make the circle breathe
In dot, change function dot() to function dot({ time }). Then replace the
circle(...) line with these two lines. Leave its heading and the scene unchanged:
const diameter = 120 + sin(time) * 40;
circle(width / 2, height / 2, diameter);
Your complete dot cell should now look like this. Use it for comparison, or
select the existing // %% patch dot heading and all its code and replace that
whole section with this block. Keep the existing scene section:
// %% patch dot
function dot({ time }) {
noStroke();
fill(255, 87, 43);
const diameter = 120 + sin(time) * 40;
circle(width / 2, height / 2, diameter);
}
Run the dot cell. The circle should slowly grow and shrink.
Thunk Machine passes information into a patch every frame. { time } picks out
the running time, measured in seconds. const diameter names the size calculated
for this frame; the next line uses that value to draw the circle.
sin(time) smoothly moves between −1 and 1,
so our diameter moves between 80 and 160 pixels.
Make these changes one at a time, running the patch after each:
- Change
sin(time)tosin(time * 2). The breathing gets twice as fast. - Change
* 40to* 15. The breathing becomes subtler. - Return to
120 + sin(time) * 40.
You now have three distinct choices: a starting size, a motion speed, and an amount of change. You can adjust any one without rewriting the rest of the drawing.
5. Move it through space
Inside dot, replace the circle(...) line with these three lines. Keep the
diameter calculation above them:
const x = width / 2 + cos(time * 0.6) * width * 0.18;
const y = height / 2 + sin(time * 0.6) * height * 0.18;
circle(x, y, diameter);
Run the cell. The breathing circle now travels around the center in an oval.
Both coordinates start at the center. cos changes its horizontal position;
sin changes its vertical position. The shared time * 0.6 keeps them moving
together. Using width and height makes the path adapt to your window.
Try it: change only the final 0.18 on the y line to 0.06. The path gets
flatter. Return it to 0.18 when you’re ready.
6. Add a second patch
Click a small + between cells, enter halo, and click Create.
Select the new // %% patch halo heading and its placeholder code, and replace
that entire section with:
// %% patch halo
function halo() {
noFill();
stroke(90, 210, 230);
strokeWeight(3);
circle(width / 2, height / 2, 240);
}
Run the new halo cell. You won’t see the ring yet. We’ve defined a patch, but
haven’t added it to the scene array yet.
Now add halo, after dot, in the existing scene array. Your scene section
should look like this:
// %% scene scene
const scene = [
() => background(12, 14, 20),
dot,
halo,
];
scene.activate();
Click inside the cell containing const scene and press Cmd/Ctrl+Enter
to apply the updated list. A thin blue ring appears at the center, with the
orange circle moving around it.
noFill() makes the ring hollow. stroke and strokeWeight set its outline color
and thickness. Each patch gets its own drawing settings, so the ring’s outline
doesn’t turn the orange circle into an outline too.
To change a shape's appearance, edit its patch and run that patch's cell.
To add, remove, or reorder shapes, edit the list in const scene = [...]
and run the cell containing that list.
7. See why order matters
Swap the dot, and halo, lines in the existing scene list so halo comes first:
// %% scene scene
const scene = [
() => background(12, 14, 20),
halo,
dot,
];
scene.activate();
Click anywhere in the code block containing const scene and press
Cmd/Ctrl+Enter. Watch where the moving circle crosses the ring: the circle
now covers the ring. Later entries draw on top of earlier ones.
Put dot before halo again and run the cell containing the list. The blue
outline crosses over the orange circle instead.
Leave the background first. If you put it last, it paints over everything you drew before it.
8. Let the layers move independently
In halo, change function halo() to function halo({ time }). Replace the
circle(...) line with:
const diameter = 240 + sin(time * 0.8) * 50;
circle(width / 2, height / 2, diameter);
Run the halo cell. The ring now breathes at its own pace while the dot continues
its orbit. You have a composed scene: two small patches, each responsible for one
visual idea, brought together by a short list.
Before moving on, make one version that feels like yours. Change the colors, slow the orbit, make the ring wider, or reduce the breathing. Change one number and watch for a few seconds before changing another.
9. Keep this first scene
- Open Performances → Current → Scenes → + Scene, enter First orbit, and choose Create scene. If Current is already selected, go straight to Scenes. This stores a named copy of the scene on your stage.
- On the First orbit scene card, choose Launch to select it. When you later edit this version, Cmd/Ctrl+S updates its saved code. The top-bar button is labeled Save; it saves the scene.
- Open Performances → All. In Unsaved performance, enter First visuals as the performance name and choose Save. A performance keeps the scenes and performance-wide settings together. Saving the performance does not overwrite each scene’s saved code; save edited scenes with Cmd/Ctrl+S first.
- On the saved First visuals performance card, open ⋯ → Export, leave JSON without attached files selected, and choose Export JSON. Keep the downloaded file as a backup. This scene has no media files to attach.
Saved work stays in this Chrome profile on this site, on this computer. A local preview and the hosted site have separate storage; another browser or profile does not share these saves. The downloaded JSON is a backup you can import elsewhere. For attached media or a whole-workspace backup, see the manual’s files and persistence reference.
Return to your scene: reopen the instrument at the same address, using the same Chrome profile. Choose First visuals from the welcome screen’s recent performances, then Start silent. If it is not listed there, choose Start silent, open Performances → All, and choose Load on the First visuals card. Open Current → Scenes and choose Launch on First orbit. Your circle and ring should return with their saved code.
You have completed the first session: two patches, a moving composition, a live edit, and a saved performance you can recall. You can stop here, or continue with the optional variations below. Switching scenes also preserves working edits during the current session, including text you have not run; Save explicitly updates the version you want to keep.
10. Make a second scene and switch between them
Optional — scene variations. Let’s keep the first version and make a slower, gold-colored variation. Open Performances → Current → Scenes and choose Launch on the First orbit scene card.
In Performances → Current → Scenes, open + Scene, enter Slow gold, and choose Create scene. This saves a copy of the current version under the new name.
Choose Launch on the Slow gold scene card before editing. Create scene saves a copy but keeps the existing scene selected. Launch selects the new copy; clicking a selected scene’s name opens Rename instead.
In the
dotfunction, replace thefill,x, andylines with these corresponding lines:fill(255, 210, 90); const x = width / 2 + cos(time * 0.25) * width * 0.18; const y = height / 2 + sin(time * 0.25) * height * 0.18;Click inside the
dotfunction and press Cmd/Ctrl+Enter. The circle turns gold and moves more slowly. Press Cmd/Ctrl+S to save this version as Slow gold.Open Performances → Current → Scenes again and choose Launch on First orbit. The stage returns to your first version, and the editor shows its code.
Choose Launch on Slow gold. The gold circle and slower movement return.
You can now switch between two looks without rewriting the code each time. Choosing Launch loads and runs the scene; you don't need to press Enter after selecting it.
Try it: switch to First orbit, then back to Slow gold while watching the stage. Each saved scene keeps its own version of the patches and drawing list.
Save a scene with Cmd/Ctrl+S after making changes you want to keep. Switching away and back also preserves working edits during the current session, but Save explicitly updates the saved version.
To include both scenes in your saved performance, open Performances → All, open your saved performance's ⋯ → Edit, and choose Save. Export it again if you want a file containing both variations.
11. Give the image a shader effect
Optional — image processing. Complete step 10 first so you have Slow gold
to copy. So far, dot and halo draw shapes. A shader effect changes the image
after those shapes have been drawn. Let's turn the smooth circles into a
blocky, pixelated image.
Start by choosing Launch on Slow gold in Performances → Current → Scenes. Use + Scene → Create scene to save a copy named Pixel orbit, then choose Launch on Pixel orbit to select it. This gives us a third variation without changing the first two.
We can apply the effect directly to the drawing list. Select the existing
// %% scene scene heading and all its code, then replace that whole section
with this block. Leave the dot and halo cells unchanged:
// %% scene scene
const scene = [
() => background(12, 14, 20),
dot,
halo,
].pixelate(40, 40);
scene.activate();
Click inside that code and press Cmd/Ctrl+Enter. The circle and ring now
have blocky edges, but they keep moving as before. The .pixelate(40, 40)
after the closing bracket processes the image drawn by the whole list.
The two numbers in .pixelate(40, 40) set the number of columns and rows in
the pixel grid. Change them to 80, 80 and run the code containing const scene
again for smaller blocks and more detail. Try 20, 20 for larger blocks.
Try applying it to just the circle: replace that same complete scene section, including its heading, with:
// %% scene scene
const scene = [
() => background(12, 14, 20),
[dot].pixelate(40, 40),
halo,
];
scene.activate();
Click inside that code and press Cmd/Ctrl+Enter. Put brackets around a patch
to give it its own image layer, then apply the effect to that layer. [dot]
draws the circle on a separate transparent layer; .pixelate(40, 40) processes
that image before it is placed into the scene. The background and ring stay
unchanged.
dot is the drawing code, not an image, so the built-in effect is written as
[dot].pixelate(...), not dot.pixelate(...). To affect both shapes together,
use [dot, halo].pixelate(40, 40) in place of their two separate scene entries.
Brackets choose what gets affected; the method chooses the effect. See the
manual's layer explanation for
the full picture.
Press Cmd/Ctrl+S to save Pixel orbit. Switch between First orbit, Slow gold, and Pixel orbit with each scene card’s Launch action to compare them. Save the performance again if you want to keep all three scenes together.
If the effect is blank or a solid color, undo the edit and run the scene again, then see the manual’s shader troubleshooting.
Your complete scene
This is the complete source from steps 1–8, before the optional gold and shader
variations. Use it for comparison or a known working replacement. To replace all working
patches and scenes, click inside an editable code cell, press Cmd/Ctrl+A, and
paste this entire program, including its // %% headings. The editor stays in the
same continuous document, with Controls and Modulations protected. Select All includes the
old patch and scene headings, but excludes managed Controls and Modulations.
Then press Cmd/Ctrl+Shift+Enter. Save work you want to keep first; replacing
working code does not delete saved scenes or performances.
For a smaller edit, change only the indicated lines as described above. For a complete section replacement, include its heading in both the selection and the replacement so it appears exactly once.
// %% patch dot
function dot({ time }) {
noStroke();
fill(255, 87, 43);
const diameter = 120 + sin(time) * 40;
const x = width / 2 + cos(time * 0.6) * width * 0.18;
const y = height / 2 + sin(time * 0.6) * height * 0.18;
circle(x, y, diameter);
}
// %% patch halo
function halo({ time }) {
noFill();
stroke(90, 210, 230);
strokeWeight(3);
const diameter = 240 + sin(time * 0.8) * 50;
circle(width / 2, height / 2, diameter);
}
// %% scene scene
const scene = [
() => background(12, 14, 20),
dot,
halo,
];
scene.activate();
You now have two reusable patches, an animated scene, and a saved performance. Next, use the composition cookbook to repeat shapes and group layers, or the current workflow guide to add controls and audio-driven modulation. The manual's Reading the code section explains the syntax used here.
For live iPhone video, follow the separate phone camera setup and troubleshooting guide. It uses the same trusted local signaling connection on both devices; camera video does not require another editor to broadcast it.