20. Webcam
Capturing
Capturing video from a webcam works pretty much the same way as reading it from a file. The createCapture function provides the basics. We declare a variable, create an instance, and read frames from it.
let capture; function setup() { createCanvas(400, 400); capture = createCapture(VIDEO); capture.hide(); } function draw() { image(capture, 0, 0, width, capture.height * width/capture.width); }

The following sketch displays the Webcam image using p5js's filter(INVERT).
let capture; function setup() { createCanvas(600, 600); capture = createCapture(VIDEO); capture.hide(); } function draw() { image(capture, 0, 0, width, width * capture.height / capture.width); filter(INVERT); }

Here is another example where we snap pictures once is a while, ala photobooth
let cam, camW, tileW, tileH, numX, numY; let cur=0; // which thumbnail is next let timer=50; // timer to the next thumbnail function setup() { createCanvas(400,400); tileW = width/4; tileH = height/4; cam = createCapture(VIDEO); camW = width/4; numX = (width-camW)/tileW; numY = height/tileH; cam.hide(); } function draw(){ image(cam,0,0,cam.width/8,cam.height/8); // grab a thumbnail once twice a second timer--; if(timer == 0) { let x = cur % numX; let y = int(cur / numX) % numY; image(cam,camW+x*tileW,y*tileH,tileW,tileH); cur++; timer = 50; } }

Processing
Since camera movies can be treated just like cameras, we can process them with any filter we would like. Once again, this is where the power of Processing comes in since this cannot be done with "canned" programs at all. The examples included in processing show lots of these examples.
The Libraries | Video (Capture) | SlitScan example unrolls time into space. A vertical column (at videoSliceX) is extracted from each frame. The columns are displayed from right to left (at drawPositionX), wrapping back around when the window is full.
Mirror and Mirror2, in the Libraries | Video (Capture) section, provide different renderings of webcam images. These are "mirrors", in that they allow us to see ourselves (albeit in a somewhat modified reflection). As part of mirroring, they also mirror image the pixels left-to-right. Mirror displays the pixels as equal-sized rectangles, rotated according to brightness, while Mirror 2 displays them as rectangles whose size depends on brightness, just like one of our examples. In the followig sketch, I will take examples processing done before and simply apply it to our camera.
// Uses camie file from Processing example Libraries | Video (Movie) | Loop import processing.video.*; // video library Capture cam; function setup() { createCanvas(640,720); smooth(); rectMode(CENTER); // load and start the camie cam = new Capture(this, 320, 240); cam.start(); } // Same as with images PImage filterContrast(PImage src) { PImage ret = new PImage(src.width,src.height); // make a new image var contrast = 0.01; // factor by which to push toward either 0 or 255 for (var i=0; i<src.pixels.length; i++) { color p=src.pixels[i]; var r=red(p), g=green(p), b=blue(p); r = constrain(r+r*contrast*(r-127.5), 0,255); g = constrain(g+g*contrast*(g-127.5), 0,255); b = constrain(b+b*contrast*(b-127.5), 0,255); ret.pixels[i] = color(r,g,b); } ret.updatePixels(); return ret; } // Same as with images PImage filterScramble(PImage src, var sx) { PImage ret = new PImage(src.width,src.height); // make a new image for (var y=0; y<src.height; y++) { for (var x=0; x<src.width; x++) { pset(ret,x,y,pget(src,var(random(x-sx,x+sx)),var(random(y-sx,y+sx)))); } } ret.updatePixels(); return ret; } function draw() { if(cam.available()) { cam.read(); cam.loadPixels(); // apply filters PImage f1 = filterContrast(cam); PImage f2 = filterScramble(cam,5); // draw frames background(0); image(cam,cam.width/2,0,cam.width,cam.height); image(f1,0,cam.height,cam.width,cam.height); image(f2,cam.width,cam.height,cam.width,cam.height); // draw a rect mosaic pushMatrix(); translate(0,cam.height*2); noStroke(); var s = 8; for (var y = s/2; y < cam.height; y += s+1) { for (var x = s/2; x < cam.width; x += s+1) { fill(pget(cam,var(x),var(y))); rect(x,y,s,s); } } popMatrix(); // draw a rect mosaic pushMatrix(); translate(cam.width,cam.height*2); noStroke(); fill(255); for (var y = s/2; y < cam.height; y += s+1) { for (var x = s/2; x < cam.width; x += s+1) { color pixel = pget(cam,var(x),var(y)); var b = brightness(pixel) / 255.0; rect(x,y,s*b,s*b); } } popMatrix(); } }
Interacting
Finally, let's make a puzzle from webcam input. The sketch works much like our earlier image-based puzzle, but we have to be able to create the same layout of rectangles each time we read another frame. Thus rather than dividing up the static image into little images and swapping them, we keep the identities of the rectangles (e.g., the rectangle that is 5 over and 2 down) and swap where they will be drawn (e.g., draw the (5,2) rectangle at (3,0) and vice-versa). Then to display the current frame, we copy rectangles (that is, copy their pixels) from the webcam to the window, according to the rectangle swapping.
The actual code is a little tricky. We identify webcam rectangles by x and y indices (in the above example, webcam rectangle (5,2)). We identify window rectangles by their linearized indices, as with the pixels array (in the example, window rectangle (3,0) becomes window rectangle 3). We use the xs and ys arrays keep track of which webcam rectangle is displayed at which window rectangle. In our example, xs[3] = 5 and ys[3] = 2, so that the (5,2) rectangle in the webcam is copied to the 3rd rectangle in the window. We copy all the rectangles with one nested loop; for each rectangle, we copy all its pixels with another nested loop.
import processing.video.*; Capture cam; var[] xs, ys; // x and y coordinates of rectangle sources // x[i],y[i] says which camera rectangle goes to window rectangle #1 PImage[] pieces; // the image fragmented into rectangles var nx=3, ny=3; // number of rectangles across and down var dx, dy; // rectangle sizes, computed from width,height and nx,ny var selX=-1,selY=-1; // if a piece has been selected, its x and y indices (-1 if none) var auto=false; // whether or not to auto swap pieces function setup() { createCanvas(640,480); noFill(); strokeWeight(3); cam = new Capture(this,640,480); cam.start(); dx = width/nx; dy = height/ny; // Initially, have our usual mapping, where piece at x+y*nx corresponds to (x,y) xs = new var[nx*ny]; ys = new var[nx*ny]; pieces = new PImage[nx*ny]; var p=0; for (var y=0; y<ny; y++) { for (var x=0; x<nx; x++) { xs[p] = x; ys[p] = y; p++; } } // Mix up the rectangles shuffle(); stroke(255,0,0); noFill(); } function draw() { if (auto && frameCount % 30 == 0) swapPieces(var(random(pieces.length)), var(random(pieces.length))); // update the images of the pieces if (cam.available()) { cam.read(); var p=0; // which window piece we're currently drawing for (var y=0; y<ny; y++) { for (var x=0; x<nx; x++) { pieces[p] = cam.get(xs[p]*dx,ys[p]*dy,dx,dy); p++; } } } // Draw the pieces at their positions var p=0; for (var j=0; j<ny; j++) { for (var i=0; i<nx; i++) { image(pieces[p],dx*i,dy*j); // Outline the piece in black stroke(0); rect(dx*+i,dy*j,dx,dy); p++; } } // Outline selected one if needed if (selX >= 0 && selY >= 0) { stroke(255,0,0); rect(dx*selX,dy*selY,dx,dy); // -1 to make sure red can be seen } } // Mix up all the pieces function shuffle() { // Swap each piece with some piece later in the array for (var i=0; i<xs.length; i++) swapPieces(i, var(random(i,xs.length))); } // Swap the pieces (both x and y values) at the two indices function swapPieces(var i1, var i2) { var x = xs[i1], y = ys[i1]; xs[i1] = xs[i2]; ys[i1] = ys[i2]; xs[i2] = x; ys[i2] = y; } function mousePressed() { // Find which piece was clicked on var px = mouseX/dx, py = mouseY/dy; // Select two pieces to swap; same piece twice to cancel if (selX >= 0 && selY >= 0) { if (selX == px && selY == py) { // same one twice -- cancel selX = -1; selY = -1; } else { // second selection -- swap swapPieces(px+py*nx, selX+selY*nx); selX = -1; selY = -1; } } else { // first selection selX = px; selY = py; } } function keyPressed() { if (key == 'a') auto = !auto; else if (key == 's') shuffle(); }