This is a music visualizer that simulates the starry night sky.
A music-loving friend of mine once told me he missed seeing the stars at night after coming to Pittsburgh. The idea for this project came as an idea for a present for that friend. I liked the idea of a portable, personal set of stars that could be charmed to life by playing music. The stars react to new notes being played, and the aurora appears at certain volume of music and duration of continuous music. (This may not seem very obvious in the video at the beginning because I wasn’t playing the notes hard enough. Also pardon my rustiness on piano – I haven’t really played in 2 years.)
The end product uses an Arduino Mega 2560, with an Electret Mic Amplifier for sound input, and loads of LEDs for display. Frequency analysis utilizes code from Adafruit’s Piccolo (https://github.com/adafruit/piccolo), which uses Elm-Chan’s FFT (Fast Fourier Transformation) library.
The creation of this project was a long and arduous process for me. My initial idea was to have a box filled with blue origami stars (http://fc04.deviantart.net/fs25/f/2008/072/f/e/Straw_Stars_by_Miraka.jpg), with white LEDs hidden inside white origami stars scattered around in the box. However, I quickly ran out of material for making the blue origami stars, and so replaced it with black cardstock and tissue paper. The end result of the stars adhere to my original idea in terms of visuals and functionality. The end product still has the white LEDs hidden inside white origami stars, and you just can’t tell clearly because they are now covered by black tissue paper. The white origami stars make the light of the white LEDs spread a little bit, and if you look carefully, the spread is in the shape of 5-pointed stars. I also wanted more white LED stars, but was limited by the number of PWM pins on the board (and later, space for the wires).
I also wanted to actually learn how to use the FFT library to implement more accurate frequency measurement, for picking out very roughly which notes are being played. It turned out that this is actually quite difficult due to harmonics, and it was hard to understand how to use the library partly due to poor documentation, so I ended up working with code from Adafruit for frequency analysis. A lot of testing was done to get it more suited for piano music. After getting the stars to work the way I wanted them to, I reflected on I could make it appear more interesting/visually appealing. The easy answer was “colors”, so I tried to implement something that appears similar to auroras. The source of the auroras are a number of LEDs. The ideal way to do this would be to use a LED strip (like this one http://www.adafruit.com/products/306), but since this was late into the project, I didn’t have time to get one.
Physically putting this together was also very hard and time-consuming. I had a lot of trouble getting the connections for all the LEDs to work. I had to basically tear my project apart several times because the conductive copper tape wasn’t effective for LEDs, or wires broke, or solder wasn’t strong enough, etc. In the end my breadboard had almost every single slot filled. Then more things fell apart as I was trying to get everything to fit inside a small box. I didn’t realize all those wires would take up so much space.
Weird, but useful tidbits I’ve learned about Arduino:
– variables with types that don’t match won’t raise an error while compiling, but would cause weird things when run
– error in uploading program to Mega board can sometimes be fixed by unplugging a few pins
In the end, I was fairly satisfied with the final product. The stars worked almost as well as I hoped they would. I just wish I was able to show off the craftsmanship that went into this project more. If I get up enough energy, I’d replace the RGB LEDs with an RGB strip. It would be difficult though, because I’d literally have to tear apart my project again, both physically and coding-wise. I enjoy watching it as someone else is playing the piano. Too bad I can’t really watch it while playing at the same time, since I have to watch the keyboard, haha.
[I just realized I accidentally named this the same as that famous van Gogh piece. Ugh. Need better naming skills.]
Code, if you’re interested. It’s messy and long and uncommented:
/* Starry Night -
a music visualizer that simulates the starry night sky.
Parts of the code are written by Adafruit Industries. Distributed under the BSD license.
See https://github.com/adafruit/piccolo for original source.
Additional code written by Jun Huo.
*/
#include
#include
#include
[I apologize for the poor quality of the pictures.]
1st idea: Starry Audio Visualizer
I wanted to make a physical audio visualizer using Arduino and multiple LED lights. The LED lights would be stored inside semi-transparent hand-made stars, which would individually light up according to noise input.
Partially inspired by these:
This is a cube made up of LED lights controlled by arduino. It is an interesting visualizer for 3D motion and objects. It is simple but effective.
This is a music visualizer made using Processing. Its display is also 3D. The white on black background makes the string of dots seem to glow, which reminds me of the night sky.
Sketch:
2nd idea: continuing Kimpi (game)
.. With enemies to kill, and overhead map in corner to show the enemies positions.
Sketch:
I realized a big part of the concept is actually very similar to this game:
http://www.funland.com/cube-runner.html
Differences are that in this game, player is supposed to avoid obstacles, whereas Kimpi should kill enemies as well as avoid them. The movement of this game is also linear, but Kimpi should be able to go in circles, theoretically.
Intended to use rotational speed on pinwheel to measure wind speed by converting angular velocity of the pinwheel to linear velocity. Looking back, I’m not sure how accurate this way of measuring wind speed is, considering properties of the pinwheel (such as friction).
Angular velocity is calculated by using a photocell to detect when a blade of the pinwheel passes over.
What it looks like:
A simple demo video that shows it working:
Fritzing board:
Code:
#include
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
Adafruit_7segment matrix = Adafruit_7segment();
int historyLength = 100;
float sensorHistory[100];
float runningL;
float runningMaxL;
float runningMinL;
float threshhold;
boolean covered;
int speedHistLen = 400;
float speedHistory[400];
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
matrix.begin(0x70);
for (int i=0; i0; i-- ){
sensorHistory[i] = sensorHistory[i-1];
}
sensorHistory[0] = sensorValue;
float currMax = -9999;
float currMin = 9999;
for (int i=0; icurrMax) { currMax = ith; }
if (ith15.0) {
if (!covered && currL< (runningL-threshhold*halfRange)) {
//If sensor is NOT covered and current sensor value falls below threshhold,
//then a blade is currently passing over
updateLinearSpeed(0);
covered = true;
}
else if (covered && currL>(runningL+threshhold*halfRange)) {
//If sensor IS covered and current sensor value falls above threshhold,
//then a blade just passed over, so update speed
updateLinearSpeed(1);
covered = false;
}
else {
updateLinearSpeed(0);
}
}
delay(1);
}
int nBlades = 8;
float radius = 0.085; //in meters
void updateLinearSpeed(int n) {
int totalPasses = 0;
for (int i=speedHistLen-1; i>0; i-- ){
speedHistory[i] = speedHistory[i-1];
totalPasses+=speedHistory[i];
}
speedHistory[0] = n;
totalPasses+=n;
float spinSpeed = 2.0*PI/nBlades*radius*((float)totalPasses)/((float)speedHistLen);
Serial.println(spinSpeed);
int displayed = (int)spinSpeed*100;
int tens = displayed/1000;
int ones = (displayed/100)%10;
int tenths = (displayed/10)%10;
int hundredths = displayed%10;
matrix.writeDigitNum(0,tens,false);
matrix.writeDigitNum(1,ones,true);
matrix.drawColon(false);
matrix.writeDigitNum(3,tenths,false);
matrix.writeDigitNum(4,hundredths,false);
matrix.writeDisplay();
}
Just learned about Geiger Counters in physics last week. I find being able to detect random radiation particles quite interesting. Not sure what I’d do with this yet, but it would be cool to play around with… if it weren’t so expensive.
RGB Color Sensor with IR filter
http://www.adafruit.com/products/1334
Having color input could be very useful. Since sight is arguably our strongest sense, I could imagine a nice program being made with color inputs from this sensor that reacts similarly to how humans would react to certain colors.
IR distance sensor includes cable (20cm-150cm)
http://www.adafruit.com/products/1031
Another sensor that might be good for user-interactive projects. Might be a good combination with RGB Color Sensor.
“Chara” is intended to be short for “character” or “characteristics”. “Chromism” is a chemistry term that stands for change in color.
This was intended to be a simulation of an over-simplified version of my observations of human group behavior.
The color of each Dot represents its core personality. In addition, each also has its own set of characteristics that influence how the Dot approaches with and is influenced by other Dots.
The simulation is not working as I intend it to yet. There are a few characteristics that I haven’t implemented yet. This is a work in progress. I would like to put the Processing program on here, but it does not run when I upload it onto OpenProcessing and I do not know why.
ArrayList allFlocks;
ArrayList allDots;
int nDots;
void setup() {
size(400, 400);
colorMode(HSB, 100);
noStroke();
nDots = 50;
allFlocks = new ArrayList();
allDots = new ArrayList();
for (int i=0; i1.0) {
ithDot.shade+=(shadeDiff*ithDot.agreeableness/100.0);
}
if (iFlock.stdev>(40.0*ithDot.openness)) {
iFlock.removeDot(ithDot);
if (iFlock.flockSize()==0) {
allFlocks.remove(iFlock);
}
Flock fnew = new Flock();
fnew.addDot(ithDot);
ithDot.flock = fnew;
}
else {
for (int j=0; j ithDot.radius && dh group; //prioritized members of group this Dot associates with
//The following are the Dot's characteristics
float agreeableness; //probability of going w/ flow of group
float extraversion; //probability of associating with other Dots
float openness; //probability of being interested in a different color
float spontaneity; //probability of randomly going off somewhere
Flock flock;
Dot (float x, float y, float s, float rad) {
position = new PVector(x, y);
float angle = random(TWO_PI);
velocity = new PVector(cos(angle), sin(angle));
acceleration = new PVector(0, 0);
shade = s;
radius = rad;
maxspeed = 1.0;
maxforce = 0.01;
group = new ArrayList();
flock = new Flock();
flock.addDot(this);
agreeableness = random(0, 1.0);
extraversion = random(0, 1.0);
openness = random(0, 1.0);
spontaneity = random(0, 1.0);
// println(str(shade)+" "+str(openness));
}
float checkCompatibility (float s) {
float lower = min(s, shade);
float higher = max(s, shade);
float shadeDist = min(higher-lower,(100-higher)+lower);
float distRatio = abs(shadeDist)/100;
return distRatio;
}
void updateFlock(Flock f) {
flock = f;
}
void run(ArrayList dots) {
flock(dots);
update();
handleBoundaries();
render();
}
void handleBoundaries() {
if (position.x > width+radius ) position.x -= width;
if (position.x < 0-radius ) position.x += width;
if (position.y > height+radius) position.y -= height;
if (position.y < 0-radius ) position.y += height;
}
void applyForce (PVector force) {
acceleration.add(force);
}
//From Flocking by Daniel Shiffman
//http://processing.org/examples/flocking.html
void flock(ArrayList dots) {
PVector sep = separate(allDots);
PVector ali = align(dots);
PVector coh = cohesion(dots);
sep.mult(1.5);
coh.mult(1.0);
ali.mult(1.0);
applyForce(sep);
applyForce(coh);
applyForce(ali);
}
// Method to update position
void update() {
// Update velocity
velocity.add(acceleration);
// Limit speed
velocity.limit(maxspeed);
position.add(velocity);
// Reset accelertion to 0 each cycle
acceleration.mult(0);
}
// A method that calculates and applies a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
PVector seek(PVector target) {
PVector desired = PVector.sub(target, position); // A vector pointing from the position to the target
// Scale to maximum speed
desired.normalize();
desired.mult(maxspeed);
// Steering = Desired minus Velocity
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxforce); // Limit to maximum steering force
return steer;
}
// Separation
// Method checks for nearby dots and steers away
PVector separate (ArrayList dots) {
float desiredseparation = 15.0f;
PVector steer = new PVector(0, 0, 0);
int count = 0;
// For every dot in the system, check if it's too close
for (Dot other : dots) {
float d = PVector.dist(position, other.position);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < desiredseparation)) {
// Calculate vector pointing away from neighbor
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
steer.div((float)count);
}
// As long as the vector is greater than 0
if (steer.mag() > 0) {
// First two lines of code below could be condensed with new PVector setMag() method
// Not using this method until Processing.js catches up
// steer.setMag(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.mult(maxspeed);
steer.sub(velocity);
steer.limit(maxforce);
}
return steer;
}
// Alignment
// For every nearby dot in the system, calculate the average velocity
PVector align (ArrayList dots) {
float neighbordist = 50;
PVector sum = new PVector(0, 0);
int count = 0;
for (Dot other : dots) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.velocity);
count++;
}
}
if (count > 0) {
sum.div((float)count);
// First two lines of code below could be condensed with new PVector setMag() method
// Not using this method until Processing.js catches up
// sum.setMag(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
sum.normalize();
sum.mult(maxspeed);
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxforce);
return steer;
}
else {
return new PVector(0, 0);
}
}
// Cohesion
// For the average position (i.e. center) of all nearby dots, calculate steering vector towards that position
PVector cohesion (ArrayList dots) {
float neighbordist = 50;
PVector sum = new PVector(0, 0); // Start with empty vector to accumulate all positions
int count = 0;
for (Dot other : dots) {
float d = PVector.dist(position, other.position);
if ((d > 0) && (d < neighbordist)) {
sum.add(other.position); // Add position
count++;
}
}
if (count > 0) {
sum.div(count);
return seek(sum); // Steer towards the position
}
else {
return new PVector(0, 0);
}
}
void render() {
fill(shade,100,100);
ellipse(position.x, position.y, radius, radius);
}
}
class Flock {
ArrayList dots;
float avgShade;
float pcx;
float pcy;
float stdev;
Flock() {
dots = new ArrayList();
avgShade = 0;
pcx = 0;
pcy = 0;
stdev = 0;
allFlocks.add(this);
}
void updateInfo(){
float newx = 0;
float newy = 0;
int n = dots.size();
float tempsum = 0;
float totalShade = avgShade*n;
for (Dot d: dots) {
PVector pos = d.position;
newx+=pos.x;
newy+=pos.y;
tempsum+=pow((totalShade-d.shade),2);
}
pcx = newx/dots.size();
pcy = newy/dots.size();
stdev = sqrt(tempsum/n);
}
void addDot(Dot d) {
//Using previous numbers to calculate new avgs for runtime
int n0 = dots.size();
float totShade0 = avgShade*n0;
int n = n0+1;
float totShade = totShade0+d.shade;
//Update the avg
avgShade = totShade/n;
d.updateFlock(this);
dots.add(d);
}
void removeDot(Dot d) {
//Using previous numbers to calculate new avgs for runtime
int n0 = dots.size();
float totShade0 = avgShade*n0;
int n = n0-1;
float totShade = totShade0-d.shade;
//Update the avg
avgShade = totShade/n;
dots.remove(d);
}
int flockSize() {
return dots.size();
}
void run() {
for (Dot d: dots) {
d.run(dots);
}
}
}
Jun’s thought process for this project:
Need something creative to project on -> ceiling lamp in room with dome shaped shades look cool -> need to find place on campus with similar lamp -> Porter has ceiling fans, ceiling fans are objects on ceiling too -> ceiling fans spin, so need to make something that plays on the spinning motion of the fan
And I ended up with something like this.
The final product did end up looking more like a planetary system than I intended to. This is just because the initial velocity of the particles are set to be tangent to the circles, and the gravity toward the center makes the particles orbit. I also didn’t foresee the projection would be projected onto the ceiling too, but I think in a way that made it look cooler. This is a projection that can’t live without the place it is projected on. AKA, if you run the program on your computer, it looks very simple, to the point of being boring/dull. However, once it is projected onto the fan, the motion of the fan makes the projection seem mesmerizing.
Code (non-Keystone version):
import pbox2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
PBox2D box2d;
ArrayList allParticles;
Surface surface;
boolean showOutline;
boolean showCircles;
float outerR;
float innerR;
void setup() {
size (600, 600);
showOutline = true;
showCircles = true;
outerR = 500;
innerR = 80;
smooth();
box2d = new PBox2D(this);
box2d.createWorld();
// box2d.setGravity(width/2, -height/2);
allParticles = new ArrayList();
surface = new Surface(innerR/2);
}
void draw() {
background(0);
float make = random(0, 1.0);
if (make<0.1) {
float angle = random(0, TWO_PI);
float x = width/2+cos(angle)*outerR/2;
float y = height/2+sin(angle)*outerR/2;
Particle p = new Particle(x, y, 5, angle);
allParticles.add(p);
}
if (keyPressed) {
if (key=='r' || key=='R') {
for (Particle p: allParticles) {
p.done();
}
allParticles.clear();
}
else if (key=='c' || key=='C') {
if (showCircles) showCircles = false;
else showCircles = true;
}
else if (key=='o' || key=='O') {
if (showOutline) showOutline = false;
else showOutline = true;
}
else if (key==CODED) {
if (keyCode==UP) {
innerR+=2;
}
else if (keyCode==DOWN) {
innerR-=2;
}
else if (keyCode==LEFT) {
outerR-=2;
}
else if (keyCode==RIGHT) {
outerR+=2;
}
}
}
if (showCircles) {
float r;
noFill();
stroke(255);
strokeWeight(3);
r = innerR+0.95*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(245);
strokeWeight(2.5);
r = innerR+0.90*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(225);
strokeWeight(2.2);
r = innerR+0.82*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(195);
strokeWeight(1.5);
r = innerR+0.70*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(155);
strokeWeight(1.0);
r = innerR+0.55*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(105);
strokeWeight(1.0);
r = innerR+0.40*(outerR-innerR);
ellipse(width/2, height/2, r, r);
stroke(45);
strokeWeight(0.8);
r = innerR+0.20*(outerR-innerR);
ellipse(width/2, height/2, r, r);
}
if (showOutline) {
surface.display();
noFill();
stroke(0, 255, 0);
strokeWeight(1);
ellipse(width/2, height/2, outerR, outerR);
}
box2d.step();
for (Particle p: allParticles) {
Vec2 pos = new Vec2();
pos = box2d.getBodyPixelCoord(p.body);
//Find distance from center
float dx = width/2-pos.x;
float dy = -height/2+pos.y;
Vec2 grav = new Vec2(dx, dy);
float dfc = sqrt(dx*dx+dy*dy);
float r0 = p.r0;
float newR = map(dfc, 0, outerR/2, r0/5, r0);
p.applyForce(grav);
// p.r = newR;
if (dfc height+r*2) {
killBody();
return true;
}
return false;
}
//
void display() {
// We look at each body and get its screen position
Vec2 pos = box2d.getBodyPixelCoord(body);
// Get its angle of rotation
float a = body.getAngle();
pushMatrix();
translate(pos.x,pos.y);
rotate(-a);
fill(100,140,200);
noStroke();
strokeWeight(1);
ellipse(0,0,r*2,r*2);
popMatrix();
}
// Here's our function that adds the particle to the Box2D world
void makeBody(float x, float y, float r) {
// Define a body
BodyDef bd = new BodyDef();
// Set its position
bd.position = box2d.coordPixelsToWorld(x,y);
bd.type = BodyType.DYNAMIC;
bd.linearDamping = 0.1f;
body = box2d.world.createBody(bd);
// Make the body's shape a circle
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
FixtureDef fd = new FixtureDef();
fd.shape = cs;
// Parameters that affect physics
fd.density = 1;
fd.friction = 0.01;
fd.restitution = 0.3;
// Attach fixture to body
body.createFixture(fd);
// Give it a random initial velocity (and angular velocity)
body.setLinearVelocity(new Vec2((50*cos(PI/2-a0)),(50*sin(PI/2-a0))));
body.setAngularVelocity(0);
}
void applyForce(Vec2 force) {
Vec2 pos = body.getWorldCenter();
body.applyForce(force, pos);
}
}
class Surface {
Body body;
float r;
Surface(float r_) {
r = r_;
makeBody(width/2,height/2,r);
body.setUserData(this);
}
void display() {
// We look at each body and get its screen position
Vec2 pos = box2d.getBodyPixelCoord(body);
pushMatrix();
noFill();
stroke(0,255,0);
strokeWeight(1);
ellipse(width/2, height/2, r*2, r*2);
popMatrix();
}
// Here's our function that adds the particle to the Box2D world
void makeBody(float x, float y, float r) {
// Define a body
BodyDef bd = new BodyDef();
// Set its position
bd.position = box2d.coordPixelsToWorld(x, y);
bd.type = BodyType.STATIC;
body = box2d.createBody(bd);
// Make the body's shape a circle
CircleShape cs = new CircleShape();
cs.m_radius = box2d.scalarPixelsToWorld(r);
FixtureDef fd = new FixtureDef();
fd.shape = cs;
// Attach fixture to body
body.createFixture(fd);
}
}
It took me a little while to realize that the projection is under water, and the black particles that travel up are bubbles. Apparently, the bubbles are created according to frequency of sound, and the movement of the bubbles are then caught by a sensor and are used to create a real-time visual projection onto the bubble wall. All elements come together to create an audiovisual experience. I find the movements of the bubbles and projection quite calming, and projection in water is kind of unique. I’m not sure why the project is named “void ()”, unless it’s the default video title if no titles are entered? But it sounds cool.
atOms – Ryo Kishi
This project is fairly different from the art projects I usually focus on. He uses air flow to suspend balls in air, and then used the rotations of the air blowers controlled by Arduino to choreograph some kind of “dance”. I find the idea and the physics quite ingenious. It seems like he is still working on this. It’d be cool to see some fancier choreography, such as incorporating varying air flow force if it’s possible, so the balls would suspend at different heights.
A very creative outtake on an old concept. This project turns eating into competitive gaming. The fork uses the difference in various fruits’ current resistance to figure out which food the player is picking up. Arduino is built into the table that shows the players what food they’re supposed to eat. The presentation of the game looks like 8-bit old-school, an homage to the old pixelated games. I’m not sure how they’re generating the food order, but I could see a potential problem with running out of a certain food on the plate. Also, although a purpose of this project is to encourage healthy eating, making eating competitive could make competitive players rush while eating (maybe not chew thoroughly before swallowing), which isn’t healthy. Anyway, I bet they had a really good time during the development of this project. Yum.
[It seems like the main focus of this project, the animation, has little to do with Arduino. The Arduino use is in the real room viewing of the video, though I’m not exactly sure what… fans, maybe? But man, the animation looked cool.] Watching this video made me feel uncomfortable in my stomach and feel captivated at the same time. The general gist looks like it’s two blobs of milk in vague shapes of beasts fighting against each other (it’s supposed to simulate human states and emotions). The animation is projected on a fabric on a custom shaped structure to make it seem more “organic”.
Was supposed to go for shattered-glass look, but shattered glass has a lot more randomness… So it turned out looking like a spider web. Heh. Had quite a bit of trouble not getting the lines to overlap, which made adding randomness a lot harder.
Updated to draw shapes using only black lines (no whites), and made the border of the shapes proportional to closeness to center. Had to review some trig to do this. Now it looks like a snowflake people can cut out just from paper. Eh..
import processing.pdf.*;
ArrayList< Arraylist > allVeins;
float defaultDP; //default percentage of length of vein
float currDP;
int defaultV; //base # of veins
int currV;
boolean recording;
void setup() {
size(864, 864);
background(255);
recording = false;
allVeins = new ArrayList< Arraylist >();
defaultDP = 1.0;
defaultV = 24;
currDP = defaultDP;
currV = defaultV;
setNewVeins(currV);
}
void setNewVeins(int n) {
allVeins.clear();
setLvl(n, 1.0);
}
void setLvl(int n, float percent) {
ArrayList veins = new ArrayList< Vein>();
for (int i=0; i lvl = allVeins.get(0);
int n = lvl.size();
beginShape();
for (int i=0; i lvl0 = allVeins.get(j);
int n = lvl0.size();
float da = 0.07;
float dd = 0.2;
ArrayList lvl1 = allVeins.get(j+1);
for (int i=0; i
(The video was laggy because my computer was having trouble handling Processing, FaceOSC, and video capture at the same time. Welp.)
I bit off a bit more than I can chew with trying to do things in 3D. I thought it would be cool to make a 3D game controlled by head movements that makes use of 3-D information provided by FaceOSC. The idea is to have a critter run on a grid and fire beams from its mouth to destroy obstacles. Properties used: face rotation, mouth height. The design of Kimpi (the critter) is supposed to include more complicated patterns, but I haven’t quite figured out how to draw them on correctly yet (too much math and pixel-manipulating). After making the general movements work, I realized that my visions of this game doesn’t really fit using FaceOSC very well – FaceOSC loses track of face easily, especially when the face is turned too much, so it is not fit for a slightly fast-paced game I wanted.
Task queue if/when time/interest allows/persists. As I was typing this, I realized this is way too ambitious even if I duplicated myself for the sole completion of this task:
– make Kimpi bounce up/down instead of glide (shouldn’t be hard)
– draw designs on Kimpi
– make keyboard controls
– include board tilt – Kimpi slides rapidly to one side
– include obstacles that can be destroyed when beam lands on them
– include enemies that actively attack Kimpi (flocking?) that can be destroyed by beam
Code for Kimpi Demo, Kimpi outward of screen and mirrors head movements. Made for testing Kimpi object class: