star trails
2016
Star Trails is a generative art project that transforms songs into visuals reminiscent of long exposure, star trail photography. Using FFT audio analysis, the script maps frequency bands to rotating points around a central axis, with each point’s brightness responding to the song’s energy in real time. As the track plays, the points orbit and fade, creating a radial visual “fingerprint” of the music.
import ddf.minim.analysis.*;
import ddf.minim.*;
Minim minim;
//AudioInput in; //uncomment to use Line in, live input
AudioPlayer in; // uncomment to use a song
FFT fft;
float value;
float newValue = 1;
float maxValue = 1;
int stroke;
int vLoc = 50;
float rate;
float songLength;
float first=0;
float add=0;
//cutoff and multiplication
int cutOff=5000;
//int cutOff=600;
float inc;
float pos=0.0;
int avg=100;
float [] averages = new float [avg];
float [] oldAverages = new float [avg];
float [] degree = new float [avg];
int [] dist = new int [avg];
IntList distances;
//stroke variables
float r=0;
float g=10;
float b=50;
float count=0;
color c = color(r, g, b);
//setup
void setup()
{
size(1000, 1000);
background (c);
//Minim setup for audio
minim = new Minim(this);
in = minim.loadFile("song4.mp3", 2048); //uncomment to use a song
in.play(); //uncomment to use a song
//in = minim.getLineIn(Minim.STEREO, 2048); //uncomment to use Line In, live audio
//start that FFTing
fft = new FFT(in.bufferSize(), in.sampleRate());
fft.window(FFT.HAMMING);
fft.linAverages(avg);
smooth();
songLength=(in.length()/1000);
rate=30*songLength;
inc=TWO_PI/rate;
frameRate(30);
distances = new IntList();
for (int i=0; i < avg; i++){
distances.append(i);
}
distances.shuffle();
for (int i = 0; i < avg; i++) {
oldAverages[i]=0;
degree[i] = radians(random(0.0, 360.0));
dist[i] = distances.get(i);
}
for (int y=0;y<height;y++){
stroke(map(y, 0, height, 0, 10), map(y, 0, height, 0, 20), map(y, 0, height, 20, 105),100);
line(0,y,width,y);
}
in.play(); //uncomment to use a song
}
//ready, set, go!
void draw()
{
if (in.isPlaying()) {
//draw updated screen
// perform a FFT on the samples in the input buffer
fft.forward(in.mix);
//pushMatrix();
//fill(map(count, 0, rate, 100, 0), map(count, 0, rate, 150, 10), map(count, 0, rate, 255, 50));
//translate(0, 0, -10);
//rect(-5, -5, width+5, height+5);
//popMatrix();
//noFill();
//count++;
strokeWeight(1);
stroke(255);
//for (int i = 0; i < height; i++)
for (int i = 0; i < fft.avgSize(); i++)
{
// fill in the new column of spectral values
averages[i]=Math.round(Math.max(0, 4*20*Math.log10(1000*fft.getAvg(i))));
if (maxValue < averages[i]) {
maxValue = averages[i];
println(maxValue);
}
//averages[i] = (int)Math.max(0, (averages[i] - map(averages[i], 0, maxValue, cutOff, 0)));
averages[i] = (int)Math.max(0, (averages[i] - map(averages[i], 0, maxValue, cutOff/Math.min((i+1), 25), 0)));
averages[i]=map(averages[i], 0, maxValue, 0, 255);
if (oldAverages[i] > averages[i]) {
averages[i]=oldAverages[i]-3;
}
}
for (int i = 0; i < fft.avgSize(); i++) {
stroke(255, averages[i]);
//stroke(constrain(averages[i], 0, 255));
strokeWeight(Math.max(0, map(averages[i], 0, 255, 0, 5)));
pushMatrix();
translate(width/2, height/2);
rotate(degree[i]);
translate(0, 0, 10);
point(0, dist[i]*10+10);
popMatrix();
oldAverages[i]=averages[i];
degree[i]+=inc;
if (averages[i]==0) {
degree[i]+=inc*5;
}
}
} else {
saveFrame("output.png");
stop();
}
}
void stop()
{
// always close Minim audio classes when you finish with them
in.close();
minim.stop();
super.stop();
}
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
cutOff+=10;
println("cutoff: " + cutOff);
} else if (keyCode == DOWN) {
cutOff-=10;
println("cutoff: " + cutOff);
}
}
}




