Skip to content

Latest commit



239 lines (166 loc) · 6.76 KB

File metadata and controls

239 lines (166 loc) · 6.76 KB

Paper Programs API

Welcome to Paper Programs! Here's what you can do:



Getting a Canvas:




Each Paper Program runs in its own Web Worker. The standard way to import other scripts into a Web Worker context is via importScripts, thus all Paper Programs first import the paperprogram API via:


If you wish to import other libraries, like d3 for example:



The following console statements will appear on the sidebar log of the editor:

console.log("hello world");

Async and Loops

We often use await statements with the API, so we usually create a top-level async context to use them in:

(async () => {
  // Code goes in here.

For looping (i.e. animations) we do not have access to requestAnimationFrame in a Web Worker, but we can use traditional setInterval:

setInterval(async () => {
  // Code goes in here.
}, 100);

Getting Things

Retrieving things from the API happens usually with the following call. We use await to wait for the promised value before proceeding:

await paper.get(...)

Canvas for Paper

This gives you a canvas to draw things over a specific paper:

const canvas = await paper.get('canvas');                   // get my paper's canvas
const canvas = await paper.get('canvas', { width: 500 });   // get canvas, with custom size
const canvas = await paper.get('canvas', { number: 1234 }); // get another paper's canvas

Canvas for Projector (a.k.a. Supporter)

This gives you a canvas over the entire projection. (Each paper gets its own, so no interference)

const supporterCanvas = await paper.get('supporterCanvas');

Get Paper Data

All papers are assigned a unique number. To get your paper's number:

const number = await paper.get('number');

This number can be used to lookup information about the paper:

const papers = await paper.get('papers');

papers[number].points   // corners of the paper {center,topLeft,topRight,bottomRight,bottomLeft}
papers[number].markers  // any dots detected inside the paper (array of {position,color})
papers[number].data     // arbitrary data set by paper.set('data')

Setting Paper Data

You can set attributes on your paper that other papers can use:

await paper.set('data', { someKey: 'someValue' });

Cover Paper in an Iframe

You can set your paper to display an iframe:

await paper.set('iframe', { src: '' });

Paper Whisker to detect nearby papers

A paper can be given a "whisker" that lets you know when it touches another paper:

const whisker = await paper.get('whisker', {direction: 'up'})

You can react to different events with a whisker

whisker.on('paperAdded', ({paperNumber, paper}) => {
  console.log('added paper', paperNumber)

whisker.on('paperRemoved', ({paperNumber, paper}) => {
  console.log('removed paper', paperNumber)

whisker.on('movedWhisker', ({ x, y }) => {
  console.log('whisker tip x : ' + x + ' y: ' + y);

You can customize a whisker:

const whisker = paper.get('whisker', {
  direction,      // "up" (default), "down", "left", "right"
  whiskerLength,  // as fraction of the side (default 0.7)
  requiredData,   // array of data fields that must be present in the other paper
  paperNumber,    // paper number to do this for (default is own paper number)
  color,          // color of the whisker (default "rgb(255, 0, 0)")

You can also change these attributes after the whisker was created:

whisker.direction = "down"
whisker.color = "green"

If you don't need a whisker any more you can remove it:


Camera Access

You can draw a region of the camera picture to another destination region (using arbitrary quadrilaterals).

For example, to draw another paper's picture to your paper:

const papers = await paper.get('papers');
const camera = await paper.get('camera');
paper.drawFromCamera(ctx, camera, papers[someNum].points, papers[myNum].points);

Marker Points

When calibrating the camera the average size of the dots is stored. Dots which are significantly bigger than the paper dots are recognized as markers.

You can request a list of all detected markers like this:

const markers = await paper.get('markers');

Each marker has the following properties:

  position: {x, y},  // global position
  color: [r, g, b],
  colorName,         // "red", "green", "blue" or "black"

  // properties which are only defined if marker is placed on a paper

  paperNumber, // number of the paper the marker is placed on
  positionOnPaper: {x, y} // normalized position of marker relative to paper origin

Working with local position

The property positionOnPaper has a value range from 0 to 1. The top left corner has the coordinate (x = 0, y = 0) and the bottom right corner has the coordinate (x = 1, y = 1). If you want to get the position of a marker on the local canvas you have to multiply the x position with the width of the canvas and the y position with the height of the canvas.

var x = canvas.width * marker.positionOnPaper.x
var y = canvas.height * marker.positionOnPaper.y

Get markers of paper

const paperNumber = await paper.get('number')
const markers = await paper.get('markers')
const markersOnPaper = markers.filter(marker => marker.paperNumber === paperNumber)