Generating things
with code

Procedural Content Generation (PCG)

programmatic generation of content
often using pseudo-random data

designing a process rather than an object

PCG are massively used in design,
games, art, vfx, architecture...

important

  1. data
  2. space
  3. process

not important

  • coding platform & language
  • coding tools & toolchain
  • capture device & data sources

what PCGs are good at

modelization

create the model of a system

simulation

use the model to process data

exploration

change the model's parameter

corollary: visualisation

rendering the model: 2D, 3D graphics,
video, tangible objects, installation...

pcg upsides

  • the computer does the boring part
  • few paramaters give many variations
  • very good at serial content
  • emergent behaviours can produce
    surprising or unexpected results

pcg downsides

  • mathematical uniqueness is often not
    enough to create significant differences
  • hard to maintain consistent variations
    while using numerous variables
  • hard to assess "good" settings
    human curation required

NO MAN'S SKY

Hello Games - no man's sky

model: universe generator

simulation: update the model with game logic

exploration: move & interact with the model

visualisation: rendering a 3D scene

KINEMATICS FOLD

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

UNNAMED SOUNDSCULPTURE

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

unnamed sound sculpture

system rules:

  • spawn a particle at a 3D location
  • make the particle fall & disappear

the particles move & disappear procedurally

this system can work for any 4D data set

1. DATA

N dimensions

continuous VS discrete

the world is a continuous system
computers process discrete data

discretizing the world

continuous data are measured
discrete data are counted

continuous => discrete

time => interval
light => color palette
reaction => process
phenomenon => simulation

N-dimensional objects

any given object can be described by
a variable number of dimensions

the values of each dimension of an object
can vary without affecting the others

light's dimensions

direction, speed, wavelength, energy

computer representation
of a unidimensional datum


the BIT
0 / 1

limitation: it can only describe whether a
given property of the object "is" or "isn't"

1D extension pack

  • bit (0/1)
  • byte / octet: 8 bits & 256 integer values
  • int, float, uint, double: 8, 16, 32, 64 bits
  • single character: 7 bits (US ASCII ), 32 bit (UTF-32)
in the computers's memory, a number
or a character is stored as an array of bits

so, technically, a number is itself
a N-dimensional object

2 dimensional data

series of unidimesnsional data
  • typed linear array: ["a", "b", "c"] / [ 0, 1, 2 ]
  • string : "Nicolas Barradeau"
  • an empty image: [ width * height ]

N-dimensional data

combinatorics of unidimensional data
  • color: { R, G, B }
  • a vector: { X, Y, Z }
  • binary pixel: { X, Y, value }
  • pixel, color: { X, Y, R, G, B }
  • pixel, color & alpha: { X, Y, R, G, B, A }
  • stereo sound: { left, right, time, sampling rate }
  • video: pixel, color, sound, time
  • 3D object: XYZ, indices, normals, colors, uvs...

?-DIMENSIONS

how many dimensions to a TWEET?
 {
"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
}
a single tweet can potentially be
represented in ~72 dimensions,
often themselves N-dimensional

data acquisition

a mobile phone

photos, videos, microphone & GPS positions
as well as some "hidden" sensors:

  • TYPE_ACCELEROMETER Motion detection (shake, tilt, etc.).
  • TYPE_AMBIENT_TEMPERATURE Monitoring air temperatures.
  • TYPE_GRAVITY Motion detection (shake, tilt, etc.).
  • TYPE_GYROSCOPE Rotation detection (spin, turn, etc.).
  • TYPE_LIGHT Controlling screen brightness.
  • TYPE_LINEAR_ACCELERATION Monitoring acceleration along a single axis.
  • TYPE_MAGNETIC_FIELD Creating a compass.
  • TYPE_ORIENTATION Determining device position.
  • TYPE_PRESSURE Monitoring air pressure changes.
  • TYPE_PROXIMITY Phone position during a call.
  • TYPE_RELATIVE_HUMIDITY Monitoring dewpoint, absolute, and relative humidity.

Open Data

  • statistical analysis friendly
  • mostly "geo-social" data
  • often geolocated

tracking devices

  • webcam
  • microphone
  • kinect
  • leap motion
  • VR devices: VIVE, Oculus

custom devices

  • Arduino boards + Processing
  • all manners of cheap sensors:
    gesture detection, heat detector,
    360° scanners, wind, rain, smoke...

synthetic data

  • noise, fractals, turbulence...
  • photogrammetry
    (build 3D models from 2D pictures)
  • CNNs & GANs (AI)

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

2. space

fields of representation

geometric

cartesian space

polar space

cylindrical space

spherical space

cartesian space 3D

color

RGB color space

HSL cylindrical space

HSL spherical space

sound

mono sound wave (2D)

stereo sound wave (3D)

higher dimensions

4D space

point in a 4D space

line in a 4D space

cube in a 4D space


RGBA (4D) color space

5.1 stereo sound system (N-D)

to represent high dimensional space data, we'll often need to project them in a lower dimension and find a way to represent the parameters that "went missing" during the projection

a color palette is a 2D projection of a 3D/4D object
here, the spectrum represents the "missing data"

in a 2D projection of a 3D mesh, the depth data is lost
a depth buffer is used to represent the "missing data"

a t-SNE reduces data dimensionality
and creates similarity clusters
making sense of high dimensional data is hard
keep the parameter count as low as possible

other spaces

physical/tangible space


spatialized sound

notes on blindness

non euclidean space


hyperbolic space

Model of Hyperbolic Space.
by Daina Taimina, 2011.

3. process

data space design

translation / rotation / scale

the basic transformations, let us
perform distributions in space

grid / frame

//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 );
        }
    }
}

circle / disc

//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 );
}

triangles

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 );
            }
        }
    }
}

fields

assign a value at given
locations in space
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 );
    });
}

point symmetry

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 );
}

projection

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 );

reflection

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);
});

extension to 3D space

just add a third dimension
grid, frame => box, extruded frame
circle, disc => torus, sphere
triangles => regular polyhedron
fields are N-Dimensional

rule-based systems

grammar

series of rules that defines all
valid sentences of a language

formal grammar

a formal grammar is based on production rules, takes
an axiom as an input and determines if it is valid

the next illustration is a "finite state machine"
it describes all the production rules relationships

picture source

instead of writing a dictionary of all the valid axioms,
we can use this FSM to ckeck if an axiom is valid

L-system

variation on formal grammar
used to model plants' growth
ruleset:
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)
L-systems introduce the concepts of
iteration, recursion, trees & graph

iteration

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();

recursion

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 );

recursive tree

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 );

graph

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
rather than the objects themselves

further considerations

random & pseudo-random

a random() methods create sequences that are - by definition - hard to reproduce. prefer a Pseudo Random Number Generator (PRNG) that produces seemingly random series of numbers

random, noise, turbulence

random values are often "too random". use a noise to soften the variations. a turbulence is an accumulation of noises sampled at various scales (octaves)

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;
}

trails & traces

preserve previously drawn frame
cheap way of adding complexity
//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);

emergent pattern

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 );

}
            

emergent behaviour

//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 );
}

parametrisation & expressiveness

drawing an ant

hyper-realistic 3D render

ANT (Atta laevigata) by javier porcel

depending on the context those are equally valid representations of an ant. use relevant parameters

graphic tools

processing

openframeworks

context free grammar

recursively applies production rules and stops when the axiom only contains terminal symbols (primitives)

structuresynth

#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
}

context free art (2d)

eisenscript demo (3D)

magicavoxel

shadertoy

fragment shader black magic

houdini

excellent series of tutorials

blender

functional scripting example

meshlab

convolutional neural networks &

generative advesrsarial networks

it's the future but for now it's mostly
academic papers with funny names:
Learning a Probabilistic Latent Space of Object Shapes via 3D Generative-Adversarial Modeling
Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks
Evolution Strategies as a Scalable Alternative to Reinforcement Learning

generative design studios

universal everything
onformative
variable.io
field.io
waltz binaire
no computer
AM-CB

twitter accounts

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

useful methods

linear interpolation

function lerp ( t, a, b ){
    return a * (1-t) + b * t;
}

normalisation

function norm( t, a, b ){
    return ( t - a ) / ( b - a );
}

mapping

function map( t, a0, b0, a1, b1 ){
    return lerp( norm( t, a0, b0 ), a1, b1 );
}

distance

function distance( p0, p1 ){
    var dx = p0.x - p1.x;
    var dy = p0.y - p1.y;
    return sqrt( dx * dx + dy * dy );
}

angle

function angle( p0, p1 ){
    return atan2( p1.y - p0.y, p1.x - p0.x );
}

polar coordinates

x = cos( angle ) * radius;
y = sin( angle ) * radius;

cylindrical coordinates

x = cos( angle ) * radius;
y = height value;
z = sin( angle ) * radius;

spherical coordinates

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;

project

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);
}

reflect

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);
}