programmatic generation of content
often using pseudo-random data
designing a process rather than an object
create the model of a system
use the model to process data
change the model's parameter
rendering the model: 2D, 3D graphics,
video, tangible objects, installation...
model: universe generator
simulation: update the model with game logic
exploration: move & interact with the model
visualisation: rendering a 3D scene
nervous systems - KINEMATICS FOLD
model: triangle based patch modeler
simulation: apply physics rules to the model
exploration: change the base model
visualisation: 3D renders & 3D print
We Are Chopchop - unnamed soundsculpture
system: use particles to represent a 3D object
simulation: playback of a recorded sequence
exploration: move a virtual camera through space
visualisation: render particles in 3D
system rules:
the particles move & disappear procedurally
this system can work for any 4D data set
N dimensions
the world is a continuous system
computers process discrete data
direction, speed, wavelength, energy
{
"coordinates": null,
"favorited": false,
"created_at": "Wed Sep 05 00:37:15 +0000 2012",
"truncated": false,
"id_str": "243145735212777472",
"entities": {
"urls": [
],
"hashtags": [
{
"text": "peterfalk",
"indices": [
35,
45
]
}
],
"user_mentions": [
]
},
"in_reply_to_user_id_str": null,
"text": "Maybe he'll finally find his keys. #peterfalk",
"contributors": null,
"retweet_count": 0,
"id": 243145735212777472,
"in_reply_to_status_id_str": null,
"geo": null,
"retweeted": false,
"in_reply_to_user_id": null,
"place": null,
"user": {
"name": "Jason Costa",
"profile_sidebar_border_color": "86A4A6",
"profile_sidebar_fill_color": "A0C5C7",
"profile_background_tile": false,
"profile_image_url": "http://a0.twimg.com/profile_images/1751674923/new_york_beard_normal.jpg",
"created_at": "Wed May 28 00:20:15 +0000 2008",
"location": "",
"is_translator": true,
"follow_request_sent": false,
"id_str": "14927800",
"profile_link_color": "FF3300",
"entities": {
"url": {
"urls": [
{
"expanded_url": "http://www.jason-costa.blogspot.com/",
"url": "http://t.co/YCA3ZKY",
"indices": [
0,
19
],
"display_url": "jason-costa.blogspot.com"
}
]
},
"description": {
"urls": [
]
}
},
"default_profile": false,
"contributors_enabled": false,
"url": "http://t.co/YCA3ZKY",
"favourites_count": 883,
"utc_offset": -28800,
"id": 14927800,
"profile_image_url_https": "https://si0.twimg.com/profile_images/1751674923/new_york_beard_normal.jpg",
"profile_use_background_image": true,
"listed_count": 150,
"profile_text_color": "333333",
"protected": false,
"lang": "en",
"followers_count": 8760,
"time_zone": "Pacific Time (US & Canada)",
"profile_background_image_url_https": "https://si0.twimg.com/images/themes/theme6/bg.gif",
"verified": false,
"profile_background_color": "709397",
"notifications": false,
"description": "Platform at Twitter",
"geo_enabled": true,
"statuses_count": 5532,
"default_profile_image": false,
"friends_count": 166,
"profile_background_image_url": "http://a0.twimg.com/images/themes/theme6/bg.gif",
"show_all_inline_media": true,
"screen_name": "jasoncosta",
"following": false
},
"source": "<a href=\"http://jason-costa.blogspot.com\" rel=\"nofollow\">My Shiny App</a>",
"in_reply_to_screen_name": null,
"in_reply_to_status_id": null
}
photos, videos, microphone & GPS positions
as well as some "hidden" sensors:
there are more and more data available and they become more and more complex (feature vectors)
reduced to sets of unidimensional variables,
these data can shift between various spaces
//regular grid with 50px cells
var cellSize = 50, i, j;
for( i = 0; i <= w; i += cellSize ){
for( j = 0; j <= h; j += cellSize ){
//grid cell indices
var cell_x = Math.round( i / cellSize );
var cell_y = Math.round( j / cellSize );
//grid
circle( i, j, 2 );
//sub-grid
if( ( cell_x >= 20 && cell_x <= 26 )
&& ( cell_y >= 8 && cell_y <= 20 ) ){
circle( i, j, 5 );
}
//frame
//horizontal lines
if( ( cell_x >= 16 && cell_x <= 30 )
&& ( cell_y == 3 || cell_y == 12 ) ){
circle( i, j, 10 );
}
//verical lines
if( ( cell_x == 16 || cell_x == 30 )
&& ( cell_y >= 3 && cell_y <= 12 ) ){
circle( i, j, 10 );
}
}
}
//points count
var count = 24;
//angle step ( angle between 2 points on the circle )
var step = Math.PI * 2 / count;
//circle radius
var radius = h / 3;
//circle distribution
for( var angle = 0; angle < Math.PI * 2; angle += step ){
//computes the X / Y position
var x = Math.cos( angle ) * radius;
var y = Math.sin( angle ) * radius;
//draw this point
circle( x, y, 10 );
}
//radial distribution
var radialSegments = 15;
for( angle = 0; angle < Math.PI * 2; angle += step * 2 ){
for( var i = 0; i < radialSegments; i++ ){
var r = i * ( radius / radialSegments);
x = Math.cos( angle ) * r;
y = Math.sin( angle ) * r;
circle( x, y, 3 );
}
}
//spiral distribution
var turns = 3;
for( angle = 0; angle < Math.PI * 2 * turns; angle += step / 4 ){
r = radius * ( angle / ( Math.PI * 2 * turns ) );
console.log( i / ( Math.PI * 2 * turns ) );
x = Math.cos( angle ) * r;
y = Math.sin( angle ) * r;
circle( x, y, 1 );
}
function reset(){
//measures of an equalateral triangle
var sides = 3;
var L = 2 * Math.sin( Math.PI / sides ); //side length
var A = L / ( 2 * Math.tan( Math.PI / sides ) ); //apothem
var H = ( 1 + A ); //radius + apothem
var size = 50;
L *= size;
H *= size;
var mx = 2 * Math.ceil( w / L );
var my = Math.ceil( h / H );
for( var i = 0; i < w; i+= mx ){
for( var j = 0; j <= h; j+= my ){
//cell indices
var cx = Math.round( i/mx );
var cy = Math.round( j/my );
//coordinates
var x = ( cx ) * L / 2;
var y = ( cy ) * H;
//triangular pattern
var mody = cy % 2;
if(( cx % 2 == 1 && cy % 2 == 0 )
|| ( cx % 2 == 0 && cy % 2 == 1 )){
circle(x, y, 5);
} else {
circle(x, y, 1);
}
//hexagonal pattern
var modx = cx % 6;
if(( mody == 0 && ( modx == 1 || modx == 3 ) )
|| ( mody == 1 && ( modx == 0 || modx == 4 ) ) ){
circle( x, y, 10 );
}
}
}
}
var HPI = Math.PI / 2;
function angle(a,b){
var dx = a[0] - b[0];
var dy = a[1] - b[1];
return Math.atan2( dy, dx );
}
//method to draw an arrow
function drawArrow( p0, p1, size ){
var a = angle( p0, p1 );
ctx.beginPath();
ctx.moveTo(p0[0], p0[1]);
ctx.lineTo(p0[0] + Math.cos( a + HPI ) * size, p0[1] + Math.sin( a + HPI ) * size);
ctx.lineTo(p1[0], p1[1]);
ctx.lineTo(p0[0] + Math.cos( a - HPI ) * size, p0[1] + Math.sin( a - HPI ) * size);
ctx.fill();
}
//this is the value at a given X/Y location
function getValue( x, y ){
return Math.cos( x ) * Math.sin( y ) + Date.now() * 0.001;
}
function reset() {
var cellSize = 50;
var points = [];
var values = [];
//regular grid with 50px cells that covers the screen
for( var i = 0; i <= w; i += cellSize ){
for( var j = 0; j <= h; j += cellSize ){
points.push( [i,j] );
values.push( getValue( i, j ) );
}
}
//draws an arrow at each point (lattice)
points.forEach( function( p, id ){
//direction
var p1 = [
p[0] + Math.cos( values[id] ) * cellSize * .75,
p[1] + Math.sin( values[id] ) * cellSize * .75
];
//draw arrow
drawArrow( p, p1, cellSize * .2 );
});
}
function rotate(p, lattice, angle){
var a = getAngle(lattice, p) + angle;
var d = getDistance(lattice, p);
var pp = new Point();
pp.x = lattice.x + Math.cos(a) * d;
pp.y = lattice.y + Math.sin(a) * d;
return pp;
}
//random walk:
// make a point move randomly X times on X / Y
// store each position in an array
var count = 64;
var steplength = 20;
var p = new Point();
var points = [];
for( var i = 0; i < count; i++ ){
p.x += steplength;
p.y += ( Math.random() - .5 ) * steplength;
//p.clone() return a copy of the point
points.push( p.clone() );
}
//performs a series of rotations around a point (center)
var center = new Point();
var rotations = 64;
var angleStep = Math.PI * 2 / rotations;
var tmp = new Point();
for( i = 0; i < rotations; i++ ){
var rotated = [];
points.forEach( function( p ){
if( i == 0 )circle(p.x,p.y, 2 );
tmp = rotate( p, center, angleStep * i );
rotated.push( tmp );
});
renderLine( rotated );
}
function project(p, a, b, asSegment) {
var dx = b.x - a.x;
var dy = b.y - a.y;
if (asSegment && dx == 0 && dy == 0) return a;
var t = ( ( p.x - a.x ) * dx + ( p.y - a.y ) * dy) / ( dx * dx + dy * dy );
if (asSegment && t < 0) return a;
if (asSegment && t > 1) return b;
return new Point(a.x + t * dx, a.y + t * dy);
}
var steplength = 30;
var p = new Point(0, h/2);
var points = [];
for( var i = 0; i <= w; i+=steplength ){
p.x = i;
p.y += ( Math.random() - .5 ) * steplength;
circle( p.x, p.y, 2 );
points.push( p.clone() );
}
renderLine( points );
//performs a series of projections onto an axis
// define by the start & end points
var start = new Point( 0, h - h / 4 );
var end = new Point( w, h - h / 4 );
points.forEach( function( p ){
var tmp = project( p, start, end );
line(p,tmp);
});
line( start, end );
//performs a series of projections onto an axis
// define by the start & end points
start = new Point( 0, 0 );
end = new Point( w, h );
points.forEach( function( p ){
var tmp = project( p, start, end );
line(p,tmp);
});
line( start, end );
function reflect(p, a, b) {
var pp = project(p, a, b);
return new Point(p.x + ( pp.x - p.x ) * 2, p.y + ( pp.y - p.y ) * 2);
}
var radius = h/3;
var step = Math.PI * 2 / 64;
var center = new Point(w/2, h/2);
var points = [];
//distributes points on a quarter of a circle
for (var i = 0; i < Math.PI / 2 + step; i += step ) {
var p = new Point(
center.x + Math.cos( i ) * radius,
center.y + Math.sin( i ) * radius
);
points.push(p.clone());
}
//vertical symmetry
var start = new Point( 0, h/2 );
var end = new Point( w, h/2 );
points.forEach(function (p) {
var tmp = reflect( p, start, end);
line(p, tmp);
});
//horizontal symmetry
start = new Point( w/2, 0 );
end = new Point( w/2, h );
points.forEach(function (p) {
var tmp = reflect( p, start, end);
line(p, tmp);
});
//diagonal symmetry
start = new Point( w/2-radius, h/2+radius );
end = new Point( w/2+radius, h/2-radius );
points.forEach(function (p) {
var tmp = reflect( p, start, end);
line(p, tmp);
});
the next illustration is a "finite state machine"
it describes all the production rules relationships
instead of writing a dictionary of all the valid axioms,
we can use this FSM to ckeck if an axiom is valid
variables : A B
axiom : A
rules : (A → AB), (B → A)
production:
n=0: A start (axiom/initiator)
/ \
n=1: A B (A→AB)
/| \
n=2: A B A (A→AB), (B→A)
/| | |\
n=3: A B A A B (A→AB), (B→A), (A→AB)
/| | |\ |\ \
n=4: A B A A B A B A (A→AB), (B→A), (A→AB), (A→AB), (B→A)
var RAD = Math.PI / 180;
//stores some vairables
var points = [];
var angles = [];
var speeds = [];
for( var i = 0; i < 20; i++ ){
//point position
points.push( new Point(w/2, h/2) );
//starting angle
angles.push( Math.random() * Math.PI * 2 );
//rotation speed
speeds.push( 5 + Math.random() * 25 );
}
ctx.beginPath();
//iterate 2000 times
for( i = 0; i < 2000; i++ ){
//iterate over all points
points.forEach( function( p, id ){
//move the pen to the current position
ctx.moveTo( p.x, p.y );
//updates angle
angles[id] += ( Math.random() - .5 ) * RAD * speeds[id];
//updates the point's position
p.x += Math.sin( angles[id] );
p.y += Math.cos( angles[id] );
//draws line to new position
ctx.lineTo( p.x, p.y );
});
}
ctx.stroke();
function draw(p0, p1, p2) {
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.closePath();
ctx.stroke();
}
function sierpinski(p0, p1, p2, count ) {
//break condition
if(count <= 0) {
//render
draw(p0, p1, p2);
}else{
//decrease the counter
count--;
//find the edges' centers
var a = [ ( p0.x + p1.x ) / 2, (p0.y + p1.y) / 2 ];
var b = [ ( p1.x + p2.x ) / 2, (p1.y + p2.y) / 2 ];
var c = [ ( p2.x + p0.x ) / 2, (p2.y + p0.y) / 2 ];
//calls the method on the new triangles
sierpinski( p0, a, c, count );
sierpinski( p1, b, a, count );
sierpinski( p2, c, b, count );
}
}
sierpinski( points[0], points[1], points[2], 6 );
function branch(length, angle) {
line(0, 0, 0, -length);
ctx.translate(0, -length);
if (length > 2) {
length *= 0.65;
ctx.save();
ctx.rotate(angle);
branch(length, angle);
ctx.restore();
ctx.save();
ctx.rotate(-angle);
branch(length, angle);
ctx.restore();
}
}
function line(x0, y0, x1, y1) {
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
}
ctx.translate(w/2,h/2 + 300);
branch( 200, Math.PI / 180 * 30 );
var Vertex = function( data ){ /*store the data*/ }
var Edge = function( v0, v1 ){
this.v0 = v0;
this.v1 = v1;
}
var Graph = function( vertices, edges ){
this.vertices = vertices;
this.edges = edges;
}
//create two vertices
var A = new Vertex( data );
var B = new Vertex( data );
//create an edge to bind the two vertices
var E = new Edge( A, B );
//create a graph
var graph = new Graph( [ A, B ], [ E ] );
this is the essence of procedural generation:
describing the rules of production
function pseudoRandom(x, y) {
return ( ( Math.sin( x * 12.9898 + y * 78.233) ) * 43758.5453123 ) % 1;
}
function noise(x, y) {
//gets the integer part
var ix = parseInt(x);
var iy = parseInt(y);
// 2D cell corners
var a = pseudoRandom(ix,iy);
var b = pseudoRandom(ix+1, iy);
var c = pseudoRandom(ix, iy+1);
var d = pseudoRandom(ix+1, iy+1);
//gets the fractional part
var fx = (x % 1);
var fy = (y % 1);
var ux = fx * fx * (3.0 - 2.0 * fx);
var uy = fy * fy * (3.0 - 2.0 * fy);
//interpolate the 4 noises with fractional parts
return (a * (1-ux) + b * ux) + (c - a) * uy * (1.0 - ux) + (d - b) * ux * uy;
}
function FBM( x, y ){
var OCTAVES = 6;
var value = 0;
var amplitude = .5;
for (var i = 0; i < OCTAVES; i++) {
value += amplitude * noise(x,y);
x *= 2.;
y *= 2.;
amplitude *= .5;
}
return value;
}
//clear half the screen
ctx.clearRect(0,0,w/2,h);
//draw a translucent rect on top of the other half
ctx.fillStyle = "#FFF";
ctx.globalAlpha = .05;
ctx.fillRect(w/2,0,w/2,h);
var morph = Date.now() * 0.0005;
var time = Date.now() * 0.001;
//points count
var total = 16;
var count = Math.min( Math.max( 1, Math.round( ( .5 + Math.cos( morph ) ) * total ) ), total );
var step = Math.PI / total;
//circle radius
var radius = h / 3;
ctx.globalAlpha = .25;
circle( 0,0, radius );
//circle distribution
for( var angle = 0; angle < count * step; angle += step ){
//computes the X / Y position
var x = Math.cos( angle ) * radius * ( Math.cos( angle + time ));
var y = Math.sin( angle ) * radius * ( Math.cos( angle + time ));
//draw this point
ctx.globalAlpha = 1;
circle( x, y, 3 );
//draw the line
ctx.globalAlpha = .25;
line( Math.cos( angle ) * radius, Math.sin( angle ) * radius,
Math.cos( angle ) * -radius, Math.sin( angle ) * -radius );
}
//tests proximity to all other boids
boids.forEach(function (other) {
//if it's the same object, bail out
if (boid == other)return;
//compute position delta
var dx = boid.x - other.x;
var dy = boid.y - other.y;
//compute boids' distance
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < boid.separation) {
separation.x += dx;
separation.y += dy;
}else{
if (dist < boid.cohesion) {
cohesion.x += dx;
cohesion.y += dy;
}
if (dist < boid.alignment) {
alignment.x += other.speed.x;
alignment.y += other.speed.y;
}
}
});
//computes the total acceleration
if( separation.length() > 0 ){
separation.normalize().multiplyScalar( separate );
boid.acceleration.add( separation );
}
if( cohesion.length() > 0 ){
cohesion.normalize().multiplyScalar( cohede );
boid.acceleration.sub( cohesion );
}
if( alignment.length() > 0 ){
alignment.normalize().multiplyScalar( align );
boid.acceleration.sub( alignment );
}
#define _col0 #F00
#define _col1 #FF0
#define _inc 0.1
40 * { y 0.1 ry 9 rz -2 s 1.01 1.01 1.01 color _col0 }column
rule column w 0.5{
{ y 0.5 ry 18 blend _col1 _inc }box
}
rule column w 0.2{
{ x -0.25 z -0.25 y 0.5 s 0.8 blend _col1 _inc } box
}
rule column w 0.2{
20 * { rx 1 ry 1.5 rz 3 s 0.75 0.95 1.10 blend _col1 _inc }box
}
tylhobbs MacTuitui P_Malin FogleBird pissang1 mystaticself M_PF Flexi23 JoanieLemercier inconvergent WilliamChyr mariuswatz xorxor_hu hyper_glu AdrienMClaireB laserberg AtticusBones _kzr aiekick wearekuva ExUtumno generateme_blog genekogan wearenocomputer _Nick_Taylor cupe_cupe eddietree albertomoss ariweinkle hughskennedy TatumCreative akirodic RavenKwok patriciogv cornusammonis mflux legomushroom roland_huf prideout p01 liabru kenji_special mattdesl lennyjpg dbtwr gordonnl kyndinfo edankwan evanbbb? novastructura thespite moebio alteredq marcinignac BlurSpline philogb wblut toxi RezaAli soulwire oosmoxiecode felixturner marpi_ subblue grgrdvrt quasimondo flight404 sougwen kcimc
function lerp ( t, a, b ){
return a * (1-t) + b * t;
}
function norm( t, a, b ){
return ( t - a ) / ( b - a );
}
function map( t, a0, b0, a1, b1 ){
return lerp( norm( t, a0, b0 ), a1, b1 );
}
function distance( p0, p1 ){
var dx = p0.x - p1.x;
var dy = p0.y - p1.y;
return sqrt( dx * dx + dy * dy );
}
function angle( p0, p1 ){
return atan2( p1.y - p0.y, p1.x - p0.x );
}
x = cos( angle ) * radius;
y = sin( angle ) * radius;
x = cos( angle ) * radius;
y = height value;
z = sin( angle ) * radius;
var theta = 0 > ? > PI * 2;
var phi = -PI > ? > PI;
x = sin(theta) * cos( phi ) * radius;
y = sin(theta ) * sin( phi ) * radius;
z = cos( theta ) * radius;
function project(p, a, b, asSegment) {
var dx = b.x - a.x;
var dy = b.y - a.y;
if (asSegment && dx == 0 && dy == 0) {
return a;
}
var t = ( ( p.x - a.x ) * dx + ( p.y - a.y ) * dy) / ( dx * dx + dy * dy );
if (asSegment && t < 0) return a;
if (asSegment && t > 1) return b;
return new Point(a.x + t * dx, a.y + t * dy);
}
function reflect(p, a, b) {
var pp = project(p, a, b);
return new Point(p.x + ( pp.x - p.x ) * 2, p.y + ( pp.y - p.y ) * 2);
}