Whole head in 3d.
To properly view this example you need WebGL capable browser. If you don't see anything please follow instructions how to get one on Khronos wiki. I checked with Firefox4b7 and WebKit nigtly build on OSX.
This example is part of blog post Cindermedusae - making generative creatures
// Code by Marcin Ignac // Made in processing.js float prevTime = millis(); float animTime = 0; float totalTime = 0; float w = 950; float h = 360; float headR = 130; float headScaleXZ = 1; float headScaleY = 1; void setup() { try { size(w, h, P3D); } catch(Exception e) { alert(e); } } int nsides = 20; int nsegments = 10; void mousePressed() { if (nsides == 20) { nsides = 40; nsegments = 20; } else { nsides = 20; nsegments = 10; } } PVertex evalHeadVertex(float theta, float phi) { PVector rvec = new PVector(headR*headScaleXZ, headR*headScaleY, headR*headScaleXZ); PVector v = new PVector(); float smoothAngleRange = radians(20); float headClampAngle = radians(120); if (theta < headClampAngle) { v.x = rvec.x * sin(theta) * sin(phi); v.y = rvec.y * cos(theta); v.z = rvec.z * sin(theta) * cos(phi); } else { if (theta >= headClampAngle && theta <= headClampAngle + smoothAngleRange) { float curveAngle = map(theta, headClampAngle, headClampAngle + smoothAngleRange, 0, PI); v.x = rvec.x * sin(theta) * sin(phi); v.y = rvec.y * cos(headClampAngle) - sin(curveAngle) * headR * 0.1; v.z = rvec.z * sin(theta) * cos(phi); } else { theta = map(theta, headClampAngle + smoothAngleRange, PI, PI/2, 0);//headClampAngle rvec.mult(0.75f); v.x = rvec.x * sin(theta) * sin(phi); v.y = rvec.y * cos(theta) - rvec.y * 0.5; v.z = rvec.z * sin(theta) * cos(phi); } } int numSpikeWaves = 6; v.x *= 1 + 0.175 * cos(phi * numSpikeWaves) * theta/PI; v.z *= 1 + 0.175 * cos(phi * numSpikeWaves) * theta/PI; return v; } void drawHead() { float theta,phi; float dtheta = PI/nsegments; float dphi = 2*PI/nsides; int segment; int side; headScaleXZ = 1 + (mouseX-width/2)/width; ArrayList vertices = new ArrayList(); ArrayList triangles = new ArrayList(); for (theta=0, segment=0; theta<=PI, segment<=nsegments; theta+=dtheta, ++segment) { for (phi=0, side=0; phi<=2*PI, side<=nsides; phi+=dphi, ++side) { PVector v1 = evalHeadVertex(theta, phi); PVector v2 = evalHeadVertex(theta+dtheta, phi); PVector v3 = evalHeadVertex(theta, phi+dphi); PVector a = PVector.sub(v2, v1); PVector b = PVector.sub(v3, v1); PVector normal = a.cross(b); normal.normalize(); if (normal.mag() == 0) { normal = new PVector(0, 1, 0); } normal.mult(10 * sin(v1.y/headR*PI + animTime*4)); v1.add(normal); vertices.add(v1); if (segment == nsegments) continue; if (side == nsides) continue; triangles.add((segment )*(nsides+1) + side); triangles.add((segment+1)*(nsides+1) + side); triangles.add((segment+1)*(nsides+1) + side + 1); triangles.add((segment )*(nsides+1) + side); triangles.add((segment+1)*(nsides+1) + side + 1); triangles.add((segment )*(nsides+1) + side + 1); } } beginShape(TRIANGLES); for(int i=0; i<triangles.size(); i++) { PVector v = vertices.get(triangles.get(i)); vertex(v.x, v.y, v.z); } endShape(); } void draw() { float time = millis(); float delta = time - prevTime; totalTime += delta/1000; prevTime = time; animTime += delta/1000; background(252, 236, 194); stroke(0); fill(252, 246, 224); //noFill(); stroke(100); float fov = PI/3.0; float cameraZ = (height/2.0) / tan(fov/2.0); perspective(fov, float(width)/float(height), cameraZ/10.0, cameraZ*10.0); translate(w/2, h/2, -100); float rotY = PI*0.3; if (mouseY) { rotY = mouseY/height * PI; } rotateX(PI/2 + rotY); rotateY(PI/3 + totalTime/5); drawHead(); }