Use Your Scenes Outside Circuitry
A JavaScript drawing, animation or game you make in Circuitry can run anywhere a web browser does: on your website, in an email attachment, on a friend's phone. This page covers exporting a scene as a web page, putting it on a website, and the pictures.js library that runs the same scene files on a page of your own.
Export as a web page
Open your scene's .js file and choose File → Export as Web Page…. Circuitry writes one .html file that holds the whole scene:
- Your program and the pictures.js library. Nobody needs Circuitry, or anything else, installed to see it.
- The files your scene uses. A file named by a path next to your scene is packed into the page: a picture (
loadImage("sprites/ship.png"),new THREE.TextureLoader().load("textures/brick.png")), a font, text or JSON (loadText,loadJSON), and a helper file loaded withuse("hud.js"). Save the.jsfile first, so the files can be found beside it. - three.js, for a 3D scene. A 2D scene leaves it out and stays small: about 60 KB, against about 850 KB for a 3D one, before its own pictures.
Open the file in any browser by double-clicking it, or send it to someone. On the web and the desktop app the file is downloaded; on iPhone and iPad you choose where to save it.
The page needs no connection: everything it runs is inside the file, and it loads nothing from anywhere else. The scene fills the window and has the keyboard from the start.
A few things do not come with it:
- A file whose path is built while the program runs, such as
loadImage("frame" + i + ".png"). Write paths out in full, in quotes, if you want the files packed in. Circuitry tells you which files it could not find. - A helper file's own helpers. A file loaded with
use()is packed; a file that one imports in turn is not. - A very large file. One file over 20 MB stops the export, with a message that names it.
- Breakpoints, the log panel and the remote-calls dialog are Circuitry's. See what is different below.
The exported file is an ordinary web page, so a website can show it: upload it like any other page and link to it, or show it inside another page with an iframe:
<iframe src="my-scene.html" style="width: 100%; aspect-ratio: 16 / 9; border: 0"></iframe>
The scene file runs as it is
The .js file is also the scene on its own: a web page runs the same file, unchanged, with the pictures.js library beside it. The rest of this page covers that. Scenes written for the library run in Circuitry too.
The library: pictures.js
pictures.js is one JavaScript file, plus a three folder that holds three.js for 3D scenes. It has an MIT licence. Its home page is the pictures.js page, where you can edit a scene and run it in your browser. The Python sibling is Pyctures.
The library also has a command for your own computer, which needs Node.js version 20 or newer and nothing else:
| Command | What it does |
|---|---|
node bin/pictures.mjs run scene.js | Opens the scene in your browser, filling the window. Save the file and it runs again. |
node bin/pictures.mjs build scene.js -o site/ | Makes a folder of plain files to put on any web host: a page, your scene and everything beside it, and pictures.js. It is the same page as Export as Web Page makes, as a folder for a web server in place of one file. |
Both are run from the library's own folder.
Put scenes on your own page
Put a <div> where each scene should go, naming its JavaScript file, and one script tag at the end of the page:
<div data-pictures-src="balls.js"></div>
<div data-pictures-src="knot.js" style="height: 420px"></div>
<script type="module" src="pictures/pictures.js"></script>
The files sit like this:
my-site/
├── index.html your page
├── balls.js your scenes
├── knot.js
└── pictures/
├── pictures.js the library
└── three/ three.js, needed only for 3D scenes
- Each scene fills its box and follows it when the box changes size, so size the
<div>with CSS like any other element. A box with no height of its own is given a 16:9 shape. - A scene's file can be in a folder, as in
data-pictures-src="scenes/game.js". The files it loads withloadImage,loadTextandloadJSONare found from the scene's own folder, not the page's. - Each scene has its own names and its own state: they cannot see each other.
- A scene's frames run only while it is in view.
draw(),update()andawait frame()wait while the scene is scrolled out of view, so a long page with many scenes only animates the ones being looked at. - The keyboard reaches a scene after a visitor clicks or taps it, so the arrow keys still scroll the page until then.
- Touch. A scene that uses the pointer (
mouse,touches,pinch, theonMouse,onTouch,onPinchandonWheelfunctions, a listener of its own, orOrbitControls) keeps a finger that is put on it, so leave some page around it to scroll by. On a scene that does not use the pointer, a finger scrolls the page as usual. - The mouse wheel scrolls the page when the pointer is over a scene, unless the scene names the wheel (
mouse.wheeloronWheel). A scene that namespinchkeeps a trackpad's pinch in the same way. - The right mouse button opens no menu over a scene that uses the pointer: the scene reads it as
mouse.button === "right". - Errors are shown along the bottom of the scene, with the line in the JavaScript file, and a Run again button.
- Nothing is loaded from anywhere else. A 2D scene needs
pictures.jsalone. A 3D scene also loads three.js from thethreefolder, once for the whole page. - The page must be served over
http://orhttps://. Opening it straight from disk does not work, because the browser will not load the scene's.jsfile that way. A page made with Export as Web Page has the scene inside it, and does open from disk.
To give a scene that is the whole page the keyboard from the start, add data-pictures-fullpage to its <div>.
Or an iframe
A scene built into a folder of its own is an ordinary web page, so another page can show it in an iframe:
<iframe src="orbit/index.html" style="width: 100%; aspect-ratio: 16 / 9; border: 0"
title="An orbit, drawn in JavaScript"></iframe>
The scene fills the iframe and follows its size.
From your own JavaScript
The module exports runScene, for a page that adds scenes itself:
import { runScene } from "./pictures/pictures.js"
const scene = runScene(document.querySelector("#game"), { src: "game.js" })
// later
scene.stop()
| Option | What it is |
|---|---|
src | The address of the scene's file, relative to the page. |
source | The scene's code as text, in place of src. |
base | Where the files the scene loads by a relative path are. Without it, they are beside the scene's file, or beside the page for a scene given as source. |
name | The file name used when working out an error's line. Without it, the name from src, or scene.js. |
keyboard | true gives the scene the keyboard from the start. |
files | A table from a path, as the scene writes it, to an address to load in its place. For a host with no folder to serve. |
fps | true shows the frame rate in a corner of the scene. |
stopStuckLoops | true stops a loop that holds the page for four seconds, with an error that names its line. |
onStatus(status) | Called with "starting", "running", "finished" or "error" when the status changes. |
onError(message, line) | Called when the scene stops with an error. line is 0 when the line is not known. |
It hands back an object with stop(), restart() and canvas. stop() ends the scene and leaves its last picture showing. restart() runs it again from the top, and restart(newSource) runs new code in its place.
What is different outside Circuitry
- A loop that never waits (
while (true)withoutawait frame()) freezes the whole page it is on. Circuitry stops such a loop after four seconds; on your own page that happens only when the page asks for it, withstopStuckLoops: truegiven torunScene. Usedraw()andupdate(), or wait in every loop. - Nothing is asked. On your own page a scene is part of that page: it can call any website the page can, and no dialog asks first.
localStoragebelongs to the website the page is on, and every scene on that site shares it. A high score kept in Circuitry does not come with the scene.- A video is made in Circuitry, with Record Video (how). The library does not record.