The hands rotate to point at the yellow dot. As each one points more towards the dot, it opens from a fist into a pointing hand. The hand sizes and locations are random: if you re-load the page, you'll get a new assortment of hands. Note that the bigger hands rotate more slowly than the small hands.
To make the source photos, I just started up my webcam one day, and recorded my hand going from a fist to a pointing finger. Then I cut away the background in Photoshop and saved the images. You could easily replace these pictures with any other kind of animation that pleases you.
Arrow[] ArrowList; // list of Arrows
color TargetColor = color(255, 255, 0); // target is yellow
float TargetX, TargetY; // location of target
void setup() {
size(600, 500); // make graphics window
smooth(); // draw things nicely
TargetX = width * random(.2, .8); // start target randomly
TargetY = height * random(.2, .8);
ArrowList = new Arrow[15]; // create the array of Arrow objects
for (int i=0; i<ArrowList.length; i++) {
// roll up some random numbers to specify a new arrow
float minR = width*.02;
float maxR = width*.1;
float radius = random(minR, maxR);
float changeSpeed = map(radius, minR, maxR, .1, .01);
float centerx = random(radius, width-radius);
float centery = random(radius, height-radius);
float startAngle = random(0, TWO_PI);
// make the new arrow
ArrowList[i] = new Arrow(centerx, centery, radius, changeSpeed, startAngle);
}
}
void draw() {
// Draw the arrows and target. If the target's moving, update the arrows.
background(200);
for (int i=0; i<ArrowList.length; i++) {
ArrowList[i].render();
}
drawTarget();
if (mousePressed) updateArrows();
}
void drawTarget() { // draw the target circle
stroke(0);
fill(TargetColor);
ellipse(TargetX, TargetY, 40, 40);
}
void mousePressed() { setTargetXY(); }
void mouseDragged() { setTargetXY(); }
void setTargetXY() {
TargetX = mouseX;
TargetY = mouseY;
}
void updateArrows() {
for (int i=0; i<ArrowList.length; i++) {
ArrowList[i].update(TargetX, TargetY);
}
}
// A class to hold an arrow object.
PImage[] ArrowHands; // images of my hand from fist to pointing
boolean GotHands = false; // did we read the hand images yet?
class Arrow {
PVector center; // center of arrow
float radius, angle, changeSpeed; // size, orientation, spin speed
color clr; // color at this moment
int imageIndex; // where we are in the animation
// construct a new Arrow
Arrow(float startX, float startY, float startRadius,
float startChangeSpeed, float startAngle) {
center = new PVector(startX, startY);
radius = startRadius;
angle = startAngle;
changeSpeed = startChangeSpeed;
clr = color(255);
imageIndex = 0;
if (!GotHands) { // read in the hand photos only once
GotHands = true;
ArrowHands = new PImage[18];
for (int i=0; i<ArrowHands.length; i++) {
String name = "arrowHandPix/hand"+nf(i, 2)+".png";
ArrowHands[i] = loadImage(name);
}
}
}
void render() {
fill(clr);
noStroke();
pushMatrix();
translate(center.x, center.y); // move to place arrow on screen
scale(radius/120.0); // Picked by eye to make nicely-sized hands
rotate(angle); // spin in place to point at the target
// photos are 397x191, so this next line centers them
image(ArrowHands[imageIndex], -199, -95);
popMatrix();
}
void update(float goalX, float goalY) {
float newAngle = atan2(goalY-center.y, goalX-center.x);
// adjust the angles so the arrow turns either clockwise or
// counter-clockwise, depending on which way gets to the goal faster
while (angle < 0) angle += TWO_PI;
while (newAngle < 0) newAngle += TWO_PI;
float diffAngle = abs(angle - newAngle);
if (diffAngle > PI) {
if (newAngle > angle) newAngle -= TWO_PI;
else angle -= TWO_PI;
}
float speed = changeSpeed;
speed = constrain(speed, .05, .3);
angle = lerp(angle, newAngle, speed);
float angleMismatch = abs(angle-newAngle); // how far off are we?
angleMismatch = constrain(angleMismatch, 0, 1);
// use the angle mismatch to make an index into the image array
imageIndex = int(ArrowHands.length * (1-angleMismatch));
imageIndex = constrain(imageIndex, 0, ArrowHands.length-1);
}
}