pictures.js: JavaScript 2D and 3D Graphics

pictures.js is for making 2D and 3D graphics and games in plain JavaScript, with no HTML to write. It is for any age and any level, a first program or a thousandth, and it is designed to bring the joy back into programming: you type a few lines and see something move. A plain .js file in the code editor can show a preview beside the code, and the file draws on it: shapes, text, pictures, moving sprites, games, charts and 3D scenes.

A few plain functions draw your first shapes. Behind them are the web's 2D canvas for 2D, and three.js for 3D, with the browser's own names. What you learn here is ordinary JavaScript graphics, and a canvas or three.js example you find elsewhere uses the same calls.

pictures.js is the JavaScript sibling of Pyctures, which does the same from Python (the Pyctures page). A program has the same shape in both, so a scene can be brought across from Python. pictures.js is also a library for your own web pages: see the pictures.js page, where you can edit a scene and run it in your browser.

You write code on one side and see the result on the other. Change the code and the preview draws it again.

function draw() {
  background("midnightblue")
  circle(width / 2, height / 2, 80, "gold")
}

That is a whole program. There is nothing to import, no HTML to write and nothing to set up: draw() is called every frame. background paints the whole preview and circle(x, y, radius, colour) draws a circle. width and height are the size of the preview, so the circle sits in the middle. For everything else there is ctx, the web's own 2D drawing surface (Drawing in 2D). These pages call the picture in the preview the canvas.

The next three steps

Change that program a little at a time. Each of these is a whole program too.

Make it move

let x = 0

function update(dt) {
  x += 200 * dt              // 200 points every second
  if (x > width) x = 0       // off the right edge: start again on the left
}

function draw() {
  background("midnightblue")
  circle(x, height / 2, 40, "gold")
}

update(dt) is called before every draw(). dt is the time since the last frame, so 200 * dt moves the circle 200 points a second on any screen. Change the 200 and see it change.

Make it answer you

let x = 200, y = 150

function update(dt) {
  if (keys.has("ArrowLeft")) x -= 300 * dt
  if (keys.has("ArrowRight")) x += 300 * dt
  if (mouse.down) {          // a mouse button, or a finger
    x = mouse.x
    y = mouse.y
  }
}

function draw() {
  background("midnightblue")
  circle(x, y, 40, "gold")
}

keys.has("ArrowLeft") is true while that key is held, and mouse is the pointer, or a finger on a touch screen. Click the preview first, so it has the keyboard.

A bit more: a small game

const basket = new Sprite("🧺", 200, 250, 64)
const stars = []
for (let i = 0; i < 5; i++) stars.push(new Sprite("⭐", random(400), random(300), 36))
let score = 0

function update(dt) {
  if (mouse.inside) basket.x = mouse.x   // the pointer is over the picture
  basket.y = height - 50
  for (const star of stars) {
    star.y += 200 * dt
    const caught = star.touches(basket)
    if (caught) score += 1
    if (caught || star.y > height) {   // caught or missed: back to the top
      star.x = random(width)
      star.y = 0
    }
  }
}

function draw() {
  background("midnightblue")
  for (const star of stars) star.draw()
  basket.draw()
  text("Score " + score, 12, 30, "white", 20)
}

A Sprite is a picture that moves, here an emoji, and touches says when two of them meet. Move the mouse, or drag a finger, to catch the stars.

The tutorials carry on from here: your first drawing, a bouncing ball, a simple game and Space Rocks. The plain functions (background, circle, rect, line, text) cover a first picture; for everything else there is ctx, the web's own 2D drawing surface (Drawing in 2D).

The quickest start: ask for one

Type a short request in Ask that names JavaScript, such as create a 2d javascript game, create a 3d game in javascript, create a 3d scene in three.js or make a javascript animation. A ready-made JavaScript file opens beside its preview, already running:

Ask forYou get
a 2D JavaScript gameStar Catcher: move a rocket, catch stars, dodge rocks, with a score and lives
a 3D game in JavaScriptCube Dodge: roll a ball left and right to dodge oncoming blocks
a 3D scene in three.js3D scene: a lit scene with shadows that you can orbit and zoom
a JavaScript animationBouncing balls: balls with trails; tap to add more

It is an ordinary JavaScript file, so you can read it and change it. The same requests without the word JavaScript, or with Python in them, open the Pyctures version.

Make a JavaScript file

  1. From the new-document menu (), choose Code, then JavaScript.
  2. Give the file a name ending in .js and create it.
  3. A new, empty JavaScript file opens as code only. Most JavaScript files are not scenes, so the preview waits until the file draws.
  4. Type or paste a program, then press Run in the footer. The preview opens beside the code and the scene starts.

Nothing has to load first. The scene is JavaScript, and it runs in the preview itself, on the device you are using.

Which files are scenes

Circuitry reads a .js file to decide whether it draws. A file is a scene when any of these is true:

  • It uses one of the names a scene is given, without making that name itself: ctx, canvas, THREE, addons, Sprite, loadImage, keys, mouse, touches, pinch, pressed or released. For example ctx.fillRect(...), new THREE.Scene(), new Sprite(...) or keys.has(...).
  • It calls one of the plain drawing functions, background(...), circle(...), rect(...), line(...) or text(...), without making a function of that name itself.
  • It waits for frames with await frame() or await flush().

A file is not a scene when it has an import or export line, or uses require(...), process. or module.exports: that is a module or a Node script. A name the file makes itself does not count either, so a web page's script that begins const ctx = ... is not taken for a scene. React files (.jsx) have their own preview.

To say that a file is a scene whatever else is in it, put a comment near the top that names pictures.js, such as // A pictures.js scene.

Names inside comments and strings do not count, so a note that mentions ctx does not turn a file into a scene.

The views

Two buttons in the editor's footer choose what you see, the same pair an HTML file and a Python file have:

ButtonWhat you see
PreviewThe preview only, filling the editor
SplitThe code and the preview side by side
Both offPlain JavaScript: only the code, and Run runs the file as an ordinary script

A file that is a scene opens split. Any other JavaScript file opens as code only. Once you pick a view yourself, it is remembered for that file on this device.

You do not have to remember to turn the preview on. If you press Run with the preview off and the file is a scene, the editor switches to split and runs it there.

Running

  • The preview follows your typing. About half a second after you stop typing, the scene starts again from the top with the new code.
  • Run starts the scene again whenever you want, for example to restart a game. With the preview off, it opens the preview.
  • A scene starts by itself when its tab comes on screen, and waits while you are on another tab. To make a file wait for Run, put // pictures: manual near the top: see Starting and stopping.
  • Restart the preview, after the Eye and Split buttons, also starts the scene again. It does not touch your code.
  • Every run starts from a clean canvas.
  • A program that only draws once finishes by itself. A program with a draw() function, an update(dt) function or a loop keeps running while its preview is open. To stop it, turn the preview off with the footer buttons.
  • When the program stops with an error, the preview shows it along its bottom edge with the line it happened on, and a Run again button.
  • Frame rate shows how many frames a second the scene is drawing, in a corner of the preview.
  • A scene that calls a website is asked about first, with Allow remote calls?. See What a scene can reach.
  • LOG in the footer shows what your program prints with console.log(), and its errors, under the preview. The first time, it asks before it starts capturing. See Finding mistakes.

Record a video

Record Video in the footer saves the preview as an MP4 video.

  1. Press and choose a size: 1080p, 4K, 720p, Shorts (1080 × 1920, upright) or Square (1080 × 1080).
  2. Choose 30 or 60 frames a second.
  3. Press Record. The scene starts again from the beginning and every frame goes into the video.
  4. Press the button again to stop and save. While it records, the button shows a red dot and how much video has been made.

Each frame in the video is exactly one frame after the one before it, however long it took to draw. A slow device takes longer to make the video, but the video itself plays smoothly. Move things by dt in update(dt), not by a fixed step, so they move at the right speed at either frame rate.

If the popup says This browser cannot make videos, record in the desktop app instead.

On a phone

A phone has room for one pane at a time.

  1. A file that is a scene opens on the preview.
  2. A Code / Preview switcher moves between the code and the preview.
  3. If a scene's file is showing its code only, tap in the footer to turn the preview on.

Touch works as the mouse does: a finger on the preview is mouse.down, and dragging moves mouse.x and mouse.y. For more than one finger, read touches and pinch.

What's in this section