with Wanjun, Amanda, and Shaun
For our first session, we met at Wanjun's apartment. Amanda had familiarized herself with the setup process the night before, and Wanjun had a space in her apartment cleared out and ready.
We got flying pretty quickly!
We opted to project on the wall instead of the floor to save time on figuring out a proper mounting setup.
- Projector on milk crates
- Webcam on tripod
- Laptop on a box
- Printer on floor
- Wall for the surface medium
- Painter's Tape for safely sticking programs to the wall
We had a Macbook with Node already installed, and started by cloning the paperprograms repo:
# Clone paperprograms
git clone https://github.com/janpaul123/paperprograms
cd paperprograms
We didn't already have Postgres installed:
# Install Postgres
brew install postgresql
# Start Postgres server
# (source: https://stackoverflow.com/a/29383787/142317)
pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
Paperprograms repo has a package-lock.json
, so I'm guessing we have to use
the new npm ci
command (see npm blog) to reliably install
correct dependencies, but since our npm version didn't support it, we just used Yarn:
# Install dependencies
yarn
# Start server
yarn run dev
With the server running, the following pages were accessible:
- http://localhost:3000/camera.html - Camera display and calibration
- http://localhost:3000/projector.html - Projector display
- http://localhost:3000/editor.html - Editor for modifying programs
http://localhost:3000/camera.html
We first opened the camera page above and granted Chrome access to our camera. Since we had the projector in mirror-mode, we caused some amusing video feedback:
http://localhost:3000/projector.html
We opened the projector page above in a new Chrome window.
To take the projector off of mirror-mode, we just clicked the Airplay icon in the taskbar and clicked "Extend Desktop".
The projector page in Chrome was just black. We dragged it to the projector's designated desktop and full-screened it.
Going back to our camera page, we dragged the corners of the red outline—marked TL, TR, BL, BR—to the outline of the projected page.
The projector page was mostly black, but it actually has a thin gray border that we used to trace the edges. You can't see the gray border below, because we dragged the red outline over it:
From our camera page, we clicked "print calibration page" button, and taped the page to our wall.
One by one, we clicked a reference color on the right side of the page, then clicked the corresponding dot on the camera.
To see the detected dots better, we had to use the "zoom" slider on the right, and pan the image by dragging it.
Extra notes:
- We didn't mess with the camera's own color calibration settings.
- The changing sun coming through our window shifted the calibrated colors a bit, so we covered the windows a bit.
- The blue painter's tape was being interpreted as a dot, so we just looped the tape onto itself to act as double-sided tape to hide behind the paper.
From our camera page, we clicked "create hello world program" button. This added a program to the print queue, which we clicked to print then taped to our wall.
The program was detected, but nothing was displaying, until we remembered to enable experimental canvas features in Chrome:
chrome://flags/ - enable "Experimental canvas features"
"Hello World" was displayed on the page!
http://localhost:3000/editor.html?9a3eed90 (hash will be different for you)
From our camera page, we copied the editor URL on the right and opened it in a new tab. The editor is hashed presumably to work for multiple camera instances—neat!
From the drop-down on this editor page, we selected the "Hello World" program, and were able to view/modify/save its code, reproduced below:
importScripts("paper.js");
(async () => {
// Get a canvas object for this paper.
const canvas = await paper.get("canvas");
// Draw "Hello world" on the canvas.
const ctx = canvas.getContext("2d");
ctx.font = "20px sans-serif";
ctx.textAlign = "center";
ctx.fillStyle = "rgb(255,0,0)";
ctx.fillText("Hello", canvas.width / 2, canvas.height / 2 - 10);
ctx.fillStyle = "rgb(0,255,0)";
ctx.fillText("world", canvas.width / 2, canvas.height / 2 + 20);
ctx.commit();
// Get a "supporter canvas", which is a canvas for the entire
// projection surface.
const supporterCanvas = await paper.get("supporterCanvas");
const supporterCtx = supporterCanvas.getContext("2d");
// Get the paper number of this piece of paper (which should not change).
const myPaperNumber = await paper.get("number");
// Repeat every 100 milliseconds.
setInterval(async () => {
// Get a list of all the papers.
const papers = await paper.get("papers");
// Clear out the supporter canvas. We get our own canvas, so we won't
// interfere with other programs by doing this.
supporterCtx.clearRect(0, 0, supporterCanvas.width, supporterCanvas.height);
// Draw a circle in the center of our paper.
const myCenter = papers[myPaperNumber].points.center;
supporterCtx.fillStyle = supporterCtx.strokeStyle = "rgb(0, 255, 255)";
supporterCtx.beginPath();
supporterCtx.arc(myCenter.x, myCenter.y, 10, 0, 2 * Math.PI);
supporterCtx.fill();
// Draw a line from our paper to each other paper.
Object.keys(papers).forEach(otherPaperNumber => {
if (otherPaperNumber !== myPaperNumber) {
const otherCenter = papers[otherPaperNumber].points.center;
supporterCtx.beginPath();
supporterCtx.moveTo(myCenter.x, myCenter.y);
supporterCtx.lineTo(otherCenter.x, otherCenter.y);
supporterCtx.stroke();
}
});
// Finally, commit to the canvas, which actually renders.
supporterCtx.commit();
}, 100);
})();
Extra notes:
- When in the editor, we realized that the view would stop updating if the Camera tab wasn't open and visible on screen.
We all wanted to start messing with Hello World, but when trying to connect to the editor URL from multiple computers, we realized only one editor was allowed to view it at a time.
So we had to create a new program for each of us to play with.
Creating New Programs: The process for creating a new program was weird. We had to go back to the Camera page, and click the "create hello world program" button for each new program we wanted to create. Then we could select it from the dropdown inside the editor.
Renaming Programs: This was done by changing the first comment line inside the code to your desired title, and saving.
Saving as New Programs: I think this was done by clicking "print as new paper" button. We ended up clicking this too many times, unknowingly creating multiple program copies.
After two hours of setup, we spent an hour playing with the code. We all tried different things in parallel while asking each other about API stuff.
We were pretty fascinated by how the canvas was being distorted when cutting the papers separated the corners, so we experimented a lot with that to see how it was working.
Amanda stretched the helloworld program (below right). Wanjun added a program to display an iframe on a page (white pokeman page below) which stretched in strange ways. We were wondering how the size was being manipulated by the corners—by stretching or proper resizing of the content.
We later played with animation of shapes and colors:
This is a raw feed from the projector image when moving the papers around:
Very excited to get it all working and to get a basic understanding of its behavior! Looking forward to next sessions where we will try a proper floor/table setup and start playing with more ideas.