Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
239 lines (166 sloc) 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)