Looking Outwards 05: 3D Computer Graphics

Artist: Don Mupasi
Work: “Adventures in Space” (2021)

Stills from “Departure”

The work I’ve chosen to look at is “Adventures in Space”, a collection of NFTs by Don Mupasi, a UK bAsed 3D visual artist and photographer.
One thing I admire is how effectively Mupasi is able to establish tone and atmosphere in his works. Each piece tells a story and the various small details catch the viewer’s attention, inviting to examine the work closer and learn more.

One of Mupasi’s major inspirations for his works include music he’s listened to or works he’s read, and these manifest in the scenes he creates. Speaking of his work, Mupasi says: “Visually, I try to convey this through scenes and compositions with a lot of color and exaggerated elements – like large suns, moons, hills, mountains, lush forests, etc. I place and move my characters in those scenes to convey certain feelings and meaning from those moments.”

“To The Moon”

The primary program he uses is Cinema 4D and After Effects. Given that his animations involve movement through space, he probably first renders the digital models in Cinema 4D and then combines them in After Effects, using layers and keyframe easing to fine tune the overall composition.

Links:
https://visualdon.uk/
https://www.skillshare.com/user/visualdon
https://foundation.app/@visualdon
https://medium.com/cryptocomnft/visualdon-discusses-his-roots-and-quitting-his-9-to-5-for-digital-art-2dc2e7282a4d

Project 4 – String Art

Clicking on the canvas allows the user to iterate through the 3 string art drawings, while holding the left mouse down shows all 3 of them overlaid on each other.

sketch
// Tsz Wing Clover Chau
// Section E

function setup() {
    createCanvas(400, 300);
    background(255);
}

var counter = 0;
var showCounter = 0;
var m = 1;
var n = 10;
var n2 = n*2;


function draw() {
    noFill();

    var cx = width/2;
    var cy = height/2;

    var dy1 = (height/n2)*m;      // dimensions for drawing 1
    var dx1 = (width/n2)*m;

    var dx2 = width/(2*n);        // dimensions for drawing 2
    var dy2 = height/(6*n);
    var dx3 = width/(6*n);
    var dy3 = height/(2*n);
    
    var size1 = min(width, height)*(4/5);         // dimensions for drawing 3
    var sx1 = width/5;
    var sy1 = height/10;
    var sx2 = width*4/5;
    var sy2 = height*9/10;

    var dx4 = size1/(2*n);
    var dy4 = dx4;

    var size2 = min(width, height)*(3/5);
    var sx3 = (width-size2)/2;
    var sy3 = (height-size2)/2;
    var sx4 = width-sx3;
    var sy4 = height - sy3

    var dx5 = size2/(2*n);
    var dy5 = dx5;

    if (counter %3 == 0){         // drawing 1
        background(255);
        for (var i = 0; i <= n2; i++){
            stroke(231, 111, 81);
            line(dx1*i, 0, width, dy1*i);
            stroke(233, 196, 106);
            line(width, dy1*i, width-(dx1*i), height);
            stroke(42, 157, 143);
            line(width - (dx1*i), height, 0, height - (dy1*i));
            stroke(38, 70, 83);
            line(0, height-(dy1*i), dx1*i, 0);
        }

    } else if (counter % 3 == 1){         // drawing 2
        background(255);

        for (var j = 0; j <= n; j++){
            stroke(38, 70, 83);
            line(cx+(dx2*j), cy+(dy2*j), width*(2/3)-(dx3*j), dy3*j);
            line(cx-(dx2*j), cy-(dy2*j), width*(2/3)-(dx3*j), dy3*j);
            stroke(233, 196, 106);
            line(cx-(dx2*j), cy-(dy2*j), (width/3)+(dx3*j), height-(dy3*j));
            line(cx+(dx2*j), cy+(dy2*j), (width/3)+(dx3*j), height-(dy3*j));
        }
    } else {                              // drawing 3
        background(255);
        for (var k = 0; k < n; k++){

            stroke(42, 157, 143);
            line(cx-(dx5*k), cy+(dy5*k), sx3+(dx5*k), sy4);
            line(cx-(dx5*k), cy+(dy5*k), sx3, sy4-(dy5*k));
            line(cx+(dx5*k), cy-(dy5*k), sx4-(dx5*k), sy3);
            line(cx+(dx5*k), cy-(dy5*k), sx4, sy3+(dy5*k));
            line(cx, sy3, sx4, sy3);
            line(sx4, sy3, sx4, cy);
            line(cx, sy4, sx3, sy4);
            line(sx3, cy, sx3, sy4);
            
            stroke(244, 162, 97);
            line(cx-(dx4*k), cy-(dy4*k), sx1+(dx4*k), sy1);
            line(cx-(dx4*k), cy-(dy4*k), sx1, sy1+(dy4*k));
            line(cx+(dx4*k), cy+(dy4*k), sx2, sy2-(dy4*k));
            line(cx+(dx4*k), cy+(dy4*k), sx2-(dx4*k), sy2);
            line(cx, sy1, sx1, sy1);
            line(sx1, sy1, sx1, cy);
            line(cx, sy2, sx2, sy2);
            line(sx2, cy, sx2, sy2);
        }
    }

    showAll();
    if (showCounter > 15){                // all 3 drawings
        background(255);
        stroke(38, 70, 83);
        for (var i = 0; i <= n2; i++){
            line(dx1*i, 0, width, dy1*i);
            line(width, dy1*i, width-(dx1*i), height);
            line(width - (dx1*i), height, 0, height - (dy1*i));
            line(0, height-(dy1*i), dx1*i, 0);
        }

        for (var k = 0; k <= n; k++){
            stroke(142, 202, 230);
            line(cx+(dx2*k), cy+(dy2*k), width*(2/3)-(dx3*k), dy3*k);
            line(cx-(dx2*k), cy-(dy2*k), width*(2/3)-(dx3*k), dy3*k);
            stroke(42, 157, 143);
            line(cx-(dx2*k), cy-(dy2*k), (width/3)+(dx3*k), height-(dy3*k));
            line(cx+(dx2*k), cy+(dy2*k), (width/3)+(dx3*k), height-(dy3*k));

            stroke(233, 196, 106);
            line(cx-(dx5*k), cy+(dy5*k), sx3+(dx5*k), sy4);
            line(cx-(dx5*k), cy+(dy5*k), sx3, sy4-(dy5*k));
            line(cx+(dx5*k), cy-(dy5*k), sx4-(dx5*k), sy3);
            line(cx+(dx5*k), cy-(dy5*k), sx4, sy3+(dy5*k));
            line(cx, sy3, sx4, sy3);
            line(sx4, sy3, sx4, cy);
            line(cx, sy4, sx3, sy4);
            line(sx3, cy, sx3, sy4);
            
            stroke(231, 111, 81);
            line(cx-(dx4*k), cy-(dy4*k), sx1+(dx4*k), sy1);
            line(cx-(dx4*k), cy-(dy4*k), sx1, sy1+(dy4*k));
            line(cx+(dx4*k), cy+(dy4*k), sx2, sy2-(dy4*k));
            line(cx+(dx4*k), cy+(dy4*k), sx2-(dx4*k), sy2);
            line(cx, sy1, sx1, sy1);
            line(sx1, sy1, sx1, cy);
            line(cx, sy2, sx2, sy2);
            line(sx2, cy, sx2, sy2);
        }
    } 
}


function mouseClicked(){
    counter ++;
}


function showAll(){
    if (mouseIsPressed){
        showCounter ++;
    } else {
        showCounter = 0;
    }
}

Looking Outwards 04 – Sound Art

Material Sequence – Physical materiality of sound
Mo H Zareei
19/11/2021

material sequencer (aluminium)
material sequencer (copper)
material sequencer (steel)

The project I’ve chosen is the “Material Sequencer: Physical materiality of sound” by Mo H Zareei. The sequencer itself is quite simple, composed mainly of 4 parts: 1, control switches; 2, onboard dial; 3, actuator/solenoid; 4, material block. The control switches inform the beat pattern within a 8 step rhythmic sequence in which the solenoid strikes the material block; the onboard dial controls the tempo of the sequence; while the material block determines the timbre of the sounds generated. By just watching it operate, the functions of each component are easily discernible, laying out the process of conversion from input to electrical to kinetic to output via sound in a clear and easily digestible manner, demystifying an otherwise ‘black box’ process. I admire the simplicity of the material artifact and the elegance with which it incorporates exploration of the sound profile of various materials. Zareei positions this work as being “a reductionist celebration of unadorned raw material through rigorous functionalism” that takes “the sequencing process outside the black box and into the acoustic realm, flaunting its materiality and physicality.”

material sequencer (wood)

From my understanding, each switch has a individual hard coded behavior(ie. only firing once every 5, 7, 10, etc. seconds), so toggling different switches at the same time allows for various combinations of beat patterns as a result of mixing different behaviors. The creator’s artistic sensibilities are inflected in the final sonic output via the choices of materials, switch combination, choice of the solenoid, and interaction with the tempo dial such that the output is able to manipulated according the user’s preferences.

Links:
https://www.creativeapplications.net/sound/material-sequencer-physical-materiality-of-sound/

Project 3 – Dynamic Drawing

Controls:
– upper right corner = scale up
– lower left corner = scale down
– up & down = triangles move to the right
– click & hold on triangle = rotate
– hover over triangle = fade to white

Originally I wanted to make a field of triangles, where the triangle & neighboring triangles would flip out into a ripple. Initial generation of the triangular grid was easy, but when I wanted to proceed with more complicated maneuvers, I found my self going back to and rewriting my code so it was more structured and centralizing all the important parameters I wanted my operations to depend on.


In particular, I really struggled with the rotation implementation. P5’s scale operation acts as a blanket technique, operating on all the geometries called after it, whereas I wanted triangles to rotate individually, meaning I had to parse through every single triangle individually to find the one I wanted to rotate. When rotating, wiggling the mouse causes the rotation to flicker, as the file is looping through draw to update the new mouse position, so keeping the mouse still when rotating pieces is advised. The rest were relatively straightforward to implement.

sketch
// Tsz Wing Clover Chau
// Section E


function setup() {
    createCanvas(600, 600);
    background(220);
    text("p5.js vers 0.9.0 test.", 10, 15);
    frameRate(200);
}

var init = true;

var row = 0;
var col = 0;
var selCol = 0;
var selRow = 0;

var offset = 30;

var n = 3;
var l = 0;


var leftX = 0;
var rightX = 0;
var ptX = 0;
var leftY = 0;
var rightY = 0;
var ptY = 0;

var mX = 0;
var mY = 0;

var wait = 70;

var sizeCounter = 1;


let neighC = [255, 255, 255];

let triList = [];


function area(x1, y1, x2, y2, x3, y3) {
    return abs(((x1*(y2-y3)) + (x2*(y3-y1))+ (x3*(y1-y2)))/2);
}

function inbounds(x1, y1, x2, y2, x3, y3, mouseX, mouseY){
    let A = area(x1, y1, x2, y2, x3, y3);
    let A1 = area(mouseX, mouseY, x2, y2, x3, y3);
    let A2 = area(x1, y1, mouseX, mouseY, x3, y3);
    let A3 = area(x1, y1, x2, y2, mouseX, mouseY);
    return (A == A1+A2+A3);
}



function draw() {
    noStroke();
    background(220);
    
    l = (width-(offset*(n-1)))/n;
    var h = sqrt(3)/2 * l; 

    // init triangle grid
    if (init){
        if (row %4 == 0){
            leftX = (col*(l+offset)) + offset;
            rightX = leftX + l;
            ptX = leftX + l/2;

            leftY = (row/2*(h+offset/2)) + offset;
            rightY = leftY;
            ptY =  leftY + h;

            midY = leftY + h/2;


        } else if (row %4 == 2) {
            leftX = ((col-0.5)*(l+offset)) + offset;
            rightX = leftX + l;
            ptX = leftX + l/2;


            leftY = ((row/2)*(h+offset/2)) + offset;
            rightY = leftY;
            ptY =  leftY + h;

            midY = leftY + h/2;

        } else if (row %4 == 3) {
            leftX = ((col - 0.5)*(l+offset)) + l/2 + offset*(3/2);
            rightX = leftX + l;
            ptX = leftX + l/2;

            leftY = (int(row/2)*(h+offset/2)) + h + offset;
            rightY = leftY;
            ptY = leftY - h; 

            midY = leftY - h/2;


        } else {
            leftX = ((col-1)*(l+offset)) + l/2 + offset*(3/2);
            rightX = leftX + l;
            ptX = leftX + l/2;

            leftY = (int(row/2)*(h+offset/2)) + h + offset;
            rightY = leftY;
            ptY = leftY - h;  

            midY = leftY - h/2;
        }
        midX = ptX;

        var cShift = false;
        var a = 90;
        let selC = [0, 0, 0];

        append(triList, [leftX, leftY, rightX, rightY, ptX, ptY, selC, cShift,
                         a, midX, midY, row, col]);

    } else {

        //controlling SCALE
        if (wait == 0 & mouseX < width/8 && mouseY < height/8){
            sizeCounter += 0.01;
        } else if (sizeCounter > 1 & mouseX > width/4 && mouseY > height*3/4){
            sizeCounter -= 0.01;
            }
        scale(sizeCounter);
        
        

        for (let i = 0; i< triList.length; i++) {
            mX = mouseX;
            mY = mouseY;
            elem = triList[i];


            fill(elem[6]);

            //controlling POSITION
            if ((mouseY > elem[2] & mouseY > elem[5]) ||
                (mouseY > elem[2] && mouseY < elem[5])){
                    elem[0] -= 0.5;
                    elem[2] -= 0.5;
                    elem[4] -= 0.5;
                    elem[9] -= 0.5;
                }

            if (inbounds(elem[0], elem[1], elem[2], elem[3], 
                         elem[4], elem[5], mX, mY)) {
                elem[7] = true;

                //controlling SHADE
                if (elem[7]){
                    for (let i = 0; i< 3; i++){
                        elem[6][i] += 10;
                    }
                }
                // controlling ROTATION  (don't move mouse during rotation - it flickers)
                if (mouseIsPressed){
                    push();
                    fill(255, 255, 255);
                    translate( elem[9], elem[10]);
                    rotate(radians(elem[8]), [elem[9], elem[10], 0]);
                    translate(-elem[9], -elem[10]);
                    triangle(elem[0], elem[1], elem[2], elem[3], elem[4], elem[5]);
                    pop();

                    elem[8] += 3;
                }
            } else {
                elem[7] = false;
            }
            if (elem[8] == 90){
                triangle(elem[0], elem[1], elem[2], elem[3], elem[4], elem[5]);
            }
        }
    }


    if (col > n){
        row ++;
        col = 0;
        if (rightY +h > height) {
        init = false;
        }
    } else {
        col ++;
    }

    if (wait > 0){
        wait --;
    }
}

Looking Outwards 03: Computational Fabrication

Aguahoja (1 – 3)
Contributors: Neri Oxman & MIT Mediated Matter Group
2018 – 2021

Physical installation at MIT Media Lab in February 2018

The project I have chosen is the Aguahoja collection (Aguahoja 1 & Aguahoja 2) by the Mediated Matter Group at MIT which are a series of pavilions and associated artifacts made from digitally fabricated biodegradable composites (ie. a cellulose-chitosan-pectin-calcium carbonate compound.
After use, these ‘structural skins’ can be programmed to degrade in water in a process called “environmental programming”.

Prototypes at MIT Media Lab in February 2018

What I admire is how ‘self-sufficient’ the project is and how integrally the ‘cradle-to-cradle’ aspect of its design concept were incorporated. The lack of need for a secondary load bearing structure conventionally needed for stability and form is forgone in favour of a material with these features embedded into the molecular structure, giving an unique elegance. I also appreciate the rigor that the researches put into development of their ‘library’ of functional biopolymers, allowing them to develop an extensive range of biocomposites that can respond to different stimuli (temp / heat, humidity, light, etc.).

According to the paper “Flow-based Fabrication: An integrated computational workflow for design and digital additive manufacturing of multifunctional heterogeneously structured objects”, published by the Mediated Matter Group in 2020, their work flow “encodes for, and integrates domain-specific meta-data relating to local, regional and global feature resolution of heterogeneous material organisations.” I interpreted it to mean that they had a mesh-free geometric primitive onto which they associated the material properties and variable flow rates of various water-based materials, and then tested/ demonstrated the physical properties of these simulations with a robot arm and multi-syringe multi nozzle deposition system. This in context of another published paper “Designing a Tree: Fabrication Informed Digital Design and Fabrication of Hierarchical Structures” implies that the biomolecules of these biomaterials are deliberately chosen to “maximize desired basic-to-acidic and hydrophobic-to-hydrophilic transitions”, while “decay maps” show the degradation of the material over time in relation to various environmental factors. Clearly, a lot of categorization and material mapping occurs on the nano scale, followed most likely by an optimization algorithm (potentially a Machine Learning program) that determines an optimal molecular organisation to be fed into robot arm & multi nozzle deposition system depending on the environmental parameters encoded for.

Robot arm depositing composite material fibers

Arguably, this is a manifestation of the “Form Follows Function” creed where an artifact/ structure’s physical form emerges as result of the functions it has to serve. This is supported by the quote “The Aguahoja 1 platform is… where shape and material composition are directly informed by physical properties (eg. stiffness and opacity), environmental conditions (eg. load, temperature, and relative humidity) and fabrication constraints (eg. degrees-of-freedom, arm, speed, and nozzle pressure), among others.” Thus, it is inferred that any artistic sensibilities exhibited by the work were encoded via the prioritization and curation of specific environmental & physical factors.

Links:
https://www.media.mit.edu/projects/aguahoja/overview/
https://www.media.mit.edu/projects/aguahoja-iii/overview/
https://web.archive.org/web/20211015194534/https://mediatedmattergroup.com/publications-2-1/2018/10/16/designing-a-tree-fabrication-informed-digital-design-and-fabrication-of-hierarchical-structures
https://web.archive.org/web/20211015184725/https://mediatedmattergroup.com/publications-1/2018/10/7/flow-based-fabrication-an-integrated-computational-workflow-for-design-and-digital-additive-manufacturing-of-multifunctional-heterogeneously-structured-objects

Project 2 – Variable Face

Inspired by the popular Sumikk Gurashi characters from Japanese company San-X, this project explores the variable faces of cute 2D characters in the context of a simple text-based meme format. The occurrence of each feature (ie. body color, lack/ type of ear, mouth, whiskers, arm position, text) are determined by their own unique variables, allowing for a wide scope of character generation.

sketch
// Tsz Wing Clover Chau
// Section E

function setup() {
    createCanvas(640, 480);
    background(220);
}


var counter = 0;
var ears = false;
var earsUp = false;
var r = 0;
var rArms = 0;
var rEars = 0;
var rMouth = 0;
var rWhiskers = 0;
var rText = 0;




let c = [255, 255, 255];
let t = ["Is for me?", "A positive weed", "Am i even\na penguin?", "Made of 1% meat, \n99% fat"];


function mouseClicked(){
    r = random([0,1,2,3]);
    rEars = random([0,1,2,3]);
    rArms = random([0,1,2,3]);
    rMouth = random([0,1,2,3]);
    rWhiskers = random([0,1,2,3]);
    rText = random([0,1,2,3]);

    print("%s", r);
    counter += 1;

    if (counter > 3){       // wrapping bounds
        counter = 0;
    }

    if (r%2 == 0){
        ears = true;
    } else {
        ears = false;
    }

    if (r == 2) {
        whiskers = true;
    } else {
        whiskers = false;
    }
}


function draw() {
    background(220);
    scale(1.45 *0.93);
    ellipseMode(CENTER);
    rectMode(CENTER);
    
    var cX = width/2;
    var cY = height/2;
    var sizeX = width/3;
    var sizeY = width/3.75;
    

    if (r == 0){
        c = [255, 255, 255];       // shirokuma
    } else if (r == 1){
        c = [202, 240, 248];       // tokage
    } else if (r == 2){
        c = [254, 250, 224];       // neko
    } else {
        c = [217, 224, 163];       // penguin
    }
    
    
    var offsetX = width/35;
    var offsetY = height/10;


    //BODY
    fill(c);
    
    rect(cX + offsetX, cY + (offsetY*2), sizeX - offsetX, height - (cY + offsetY), 70);
    ellipse(cX, cY, sizeX, sizeY);

    noStroke();
    rect(cX + offsetX, cY + (offsetY*2)-2, sizeX - offsetX-2, height - (cY + offsetY)-5, 70);
    
    stroke(0);
    strokeWeight(3);
    beginShape();
    curveVertex(cX - sizeX/1.75, cY - sizeY*0.9);            //1st control point
    curveVertex(cX + sizeX/2.5, cY - offsetY*1.07);
    curveVertex(cX + sizeX/1.5, cY + (offsetY*2));
    curveVertex(cX + sizeX/3.75, height - offsetY*1.02);
    curveVertex(cX - sizeX/1.75, cY + sizeY*0.7);            //2nd control point
    endShape();

    //ARMS
    if (rArms == 0){
        arc(cX, cY + offsetY*1.5, offsetY, offsetX*1.2, PI / 2, 3 * PI / 2, OPEN);
        arc(cX - offsetY, cY + offsetY*1.5, offsetY, offsetX*1.2, 3*PI / 2, PI / 2, OPEN);
    } else if (rArms %3 == 0){
        arc(cX - offsetY, cY + offsetY*1.5, offsetY, offsetX*1.2, 3*PI / 2, PI / 2, OPEN);
    } else if (rArms == 1){
        arc(cX, cY + offsetY*1.5, offsetY, offsetX*1.2, PI / 2, 3 * PI / 2, OPEN);
    } else {
        arc(cX, cY + offsetY*1.5, offsetY, offsetX*1.2, PI / 2, 3*PI / 2, OPEN);
        arc(cX - offsetX*4.25, cY + offsetY*1.5, offsetY, offsetX*1.2, PI / 2, 3*PI / 2, OPEN);
    }
    

    //EYES
    push();
    fill(0,0,0);
    ellipse(cX - offsetY*1.2, cY - offsetY/6, offsetX/2, offsetX/3);
    ellipse(cX + offsetY*0.2, cY - offsetY/6, offsetX/2, offsetX/3);
    pop();

    //NOSE / MOUTH
    push();
    if (rMouth %3 == 0){                        // beak
        fill(255, 220, 116);
        ellipse(cX - offsetY/2, cY+offsetX - offsetY/6, offsetY/1.5, offsetX);
        line(cX - offsetY/1.25, cY + offsetY/5, cX - offsetY/5, cY + offsetY/5);
    } else if (rMouth %2 == 0) {               // snout
        fill(255);
        strokeWeight(2);
        rect(cX - offsetY/2, cY+offsetX*1.15, offsetY*0.85, offsetX*1.4, 120);
        fill(0);
        ellipse(cX - offsetY/2, cY+offsetX - offsetY/6, offsetY/3, offsetX/1.75);
    } else {
        fill(0);
        ellipse(cX - offsetY/2, cY+offsetX - offsetY/6, offsetY/3, offsetX/1.75);
    }
    pop();


    //EARS
    fill(c);
    if (rEars %2 == 0){           // bear
        push();
        rotate(radians(15));
        arc(cX + offsetY*1.8, cY - sizeY, offsetY*1.1, offsetY, -PI*0.95, PI*0.05, OPEN); 
        fill(252, 213, 206);
        ellipse(cX + offsetY*1.8, cY - sizeY*1.02, offsetY*0.45, offsetY/4);
        pop();

        push();
        rotate(radians(-20));
        arc(cX - sizeX/1.5, cY + offsetX/1.75, offsetY*1.1, offsetY, -PI*1.1, -PI*0.03, OPEN);
        fill(252, 213, 206);
        ellipse(cX - sizeX/1.5, cY + offsetX/2.2, offsetY*0.45, offsetY/4);
        pop();
    } else if (rEars%3 ==  0){     // cat
        push();
        rotate(radians(-20));
        arc(cX - sizeX*0.85, cY + offsetX*1.5, offsetY*1.35, offsetY*1.15, -PI*0.7, -PI*0.1, PIE);
        noStroke();
        arc(cX - sizeX*0.837, cY + offsetX*1.6, offsetY*1.25, offsetY*1.05, -PI*0.7, -PI*0.1, PIE);
        
        
        stroke(0);
        arc(cX - sizeX*0.25, cY + offsetX*3.85, offsetY*1.35, offsetY*1.15, -PI*0.7, -PI*0.1, PIE);
        noStroke();
        arc(cX - sizeX*0.235, cY + offsetX*3.95, offsetY*1.25, offsetY*1.05, -PI*0.7, -PI*0.1, PIE);
        pop();
    }

    //WHISKERS
    if (rWhiskers %3 == 0 ){
        line(cX - offsetY*2.4, cY, cX - offsetX*5.5, cY + offsetY*0.05);
        line(cX - offsetY*2.4, cY + offsetX/2, cX - offsetX*5.5, cY + offsetX/2 - offsetY*0.05);
        line(cX + offsetY, cY, cX + offsetX*2, cY + offsetY*0.05);
        line(cX + offsetY, cY + offsetX/2, cX + offsetX*2, cY + offsetX/2 - offsetY*0.05);
    }
    
    
    push();
    textSize(18);
    noStroke();
    fill(255,255, 255);
    text(t[rText], width/14, height/2);
    pop();

}

Looking Outwards 02: Generative Art

Transiterate
Holger Lippman
2022

Spring Storm
Holger Lippman
2020-2021

The projects I have chosen are Holger Lippmann’s “Transiterate” (2022) and “Spring Storm” (2020-2021). “Transiterate” is an animated looping NFT consisting of 100 individual unique frames. Lippmann’s site describes it as “a gradual, repeating subdivision (iteration) of rhythmically consecutive color tables”, which translates to a large circle with constantly shifting and transforming subdivisions whose coloring move up and down the color wheel.

Meanwhile, “Spring Storm” is a series of prints composed of red and white swirls on a wavy background. The compositions were generated via layered Perlin noise, resulting in a highly dynamic yet varied series of artworks. The integration of code and other digital means as a core part of a lucrative artistic workflow is highly admirable.

Spring Storm (2021) – Holger Lippmann

Lippmann’s work is described as falling “somewhere in between artistic disassociation and realistic reproduction, between old values and modern perspectives”. His abstract compositions are reminiscent of the atmosphere and sensorial experience of various natural phenomena. He incorporates various algorithms, particularly RNG, which allows him to instantly generate “an abundance of possibilities”, which he feels is missing in traditional forms of media.

Links:
https://holgerlippmann.io/
https://www.lumas.com/artist/holger_lippmann/
http://e-art.co/

Project 1- My Self Portrait

sketch
function setup() {
    createCanvas(600, 600);
    background(120);
    //text("p5.js vers 0.9.0 test.", 10, 15);
}

function draw() {
    background(120);
    strokeCap(ROUND);
    scale(0.93);

    //DYNAMIC BACKGROUND
    noFill();
    noStroke();
    var c = width/2;
    var alt = true;

    if (mouseX >= c){
        alt = true;
        fill(244, 162, 97);             // orange
        triangle(0,0, c, c, 100, -50); 

        fill(38, 70, 83);               // dark blue
        triangle(400, -50, c,c, 600, -50);
        
        fill(42, 157, 143);             //teal
        triangle(-200, 300, c, c, -300, 400);
        triangle(650, 20, c,c, 650, 50);

        fill(233, 196, 106);            // yellow
        triangle(-400, 400, c, c, -600, 650);
        triangle(650, 200, c, c, 650, 340);

        fill(231, 111, 81);             // red
        triangle(100, 650, c, c, 350, 650);
        triangle(300, -50, c, c, 380, -50);

        fill(96, 108, 56);              // green
        triangle(650, 400, c, c, 650, 500);

    } else {
        alt = false;
        fill(40, 54, 24);             // dark green
        triangle(0,30, c, c, 0, 250); 

        fill(254, 250, 224);             // cream
        triangle(400, -50, c,c, 600, -50);
        
        fill(221, 161, 94);             //tan
        triangle(-200, 300, c, c, -300, 400);
        triangle(600, 0, c,c, 650, 0);

        fill(43, 45, 66);            // dark purple
        triangle(-400, 500, c, c, 0, 580);
        triangle(650, 200, c, c, 650, 340);

        fill(131, 197, 190);             // light blue
        triangle(0, 650, c, c, 80, 650);
        triangle(100, 0, c, c, 210, 0);

        fill(255, 221, 210);              // salmon
        triangle(650, 500, c, c, 650, 650);
    }

    //FACE 
    noStroke();
    fill(255, 255, 255);

    beginShape();
    vertex(147, 240);
    vertex(147, 290);
    vertex(153, 295);

    vertex(300, 300);
    vertex(195, 434);
    vertex(203, 460);
    vertex(208, 490);
    vertex(209, 510);
    vertex(208, 530);
    vertex(201, 570);
    vertex(181, 645);
    vertex(518, 645);
    vertex(645, 645);
    vertex(645, 540);
    vertex(550, 538);
    vertex(550, 497);
    vertex(500, 250);
    endShape();

    stroke(10);
    
    bezier(280, 519, 210, 500, 90, 310, 228, 163);
    bezier(228, 163, 287, 95, 390, 117, 422, 147);
    bezier(422, 147, 600, 320, 370, 540, 280, 519);
    noStroke();
    triangle(280, 519, 228, 163, 422, 147);

    //NOSE
    stroke(7);
    noFill();
    bezier(261, 339, 236, 321, 260, 315, 269, 295);
    bezier(269, 295, 275, 254, 278, 228, 267, 212);
    bezier(321, 338, 334, 320, 309, 310, 297, 282);
    bezier(324, 324, 312, 305, 289, 304, 297, 282);

    //MOUTH
    bezier(238, 396, 253, 369, 309, 374, 351, 400);
    bezier(238, 396, 260, 474, 331, 433, 351, 400);

    //HAIR
    stroke(10);
    fill(255);
    curve(-500, 1000, 291, 83, 550, 538, -200, 1003);
    curve(600, 600, 291, 83, 147, 290, 620, 250);
    //curve(1000,-700, 550, 497, 490, 645, 0, 0);
    bezier(291, 123, 460, 90, 380, 320, 500, 350);
    noFill();
    bezier(195, 434, 220, 500, 210, 550, 180, 645);
    curve(305, 105, 291, 123, 165, 297, 800, 193);
    
    noStroke();

    fill(255, 255, 255);
    triangle(291, 83, 220, 180, 291, 123);
    triangle(291, 83, 350, 115, 291, 122);

    //NECK
    stroke(7);
    bezier(255, 507, 290, 550, 260, 620, 247, 645);
    noFill();
    bezier(425, 442, 460, 530, 460, 540, 645, 540);
    
    //EYES
    fill(237, 189, 64, 200);
    noStroke();
    ellipseMode(CENTER);
    circle(353, 246, 23);
    ellipse(222, 251, 18, 20);

    stroke(8);
    noFill();
    bezier(331, 257, 347, 220, 391, 230, 409,  249);
    bezier(257, 256, 231, 228, 210, 241, 206, 261);


    //TEXT
    var s = 'Ceci n\'est pas un autoportrait.';
    var f = 'Is this a self portrait?'
    fill(0);
    if (alt){
        textSize(30);
        text(s, 150, 50);
    } else {
        textSize(20);
        fill(255);
        noStroke();
        text(f, 50, 50, 70);
    }

}

LO: My Inspiration

The project I’d like to talk about is Supergiant Games’ Hades, a video game where you play as Zagreus, Prince of the Underworld, on a quest to battle his way out of the Underworld and meet his mother. It was named the 2020 Game of the Year, and is a rogue-like dungeon crawler meaning principally 2 things: 1. Progress is reset when you die, 2. Each run is different because the sequence of rooms you go through is randomized.

Rogue-likes are notorious for being mechanically difficult and requiring alot of repetition to master and ultimately enjoy. Despite this, Supergiant managed to implement a rather sophisticated and intricate dialogue tier system a involving a cast of 29 characters and 20000 unique lines of voiced dialogue. The integrated storytelling, beautiful art style, atmospheric soundtrack, combined with the baseline permanent progression and customizable build systems, bolster the games’ replayability such that even 2 years later people are still playing and producing content centered around Hades’.


The development time for Hades was 3 years, which is standard for the video game industry. Supergiant is a small independent indie game company based in San Francisco, comprised of around 20 people.

The thing that is astounding about the Supergiant team is that they do not ‘crunch’, or undergo compulsory overtime during game development, which is another industry standard. Their approach to project management and internal team relations are well planned out, flexible and healthy, resulting in a high employee retention rate, which is incredibly rare.

Almost all notable assets are either custom or created in-house, from the art to the music to even the game engine which is based on the Microsoft XNA framework, written in C# and was rewritten for better game performance and cross platform support during the development of Hades.


Hades is described as “a reaction to Pyre, which was a reaction to Transistor, which was a reaction to Bastion”, all of which are the studio’s previous games and inform different aspects of the game, from overall workflow and anti-burnout failsafes, to the top-down isometric camera position, to the idea of continuing narration and story across multiple attempts, to offering different abilities.

Given the way the team approaches games and game design, the next game Supergiant will release will develop on the themes, mechanics and storytelling devices explored in Hades and their previous projects, such as dungeon-crawlers and procedural room generation, or non-linear storytelling. Which one exactly that will be, is difficult to say, as Supergiant have yet to release any details on their blog.

Authors: Supergiant Games (Amir Rao, Gavin Simon, Darren Korb, Jen Zee, Greg Kasavin, Andrew Wang, Logan Cunningham, Josh Barnett, John-Paul Gabler, Dexter Friedman, Eduardo Gorinstein, Alice Lai, Joanne Tran, Caitlin Sales, Will Turnbull, Nikola Sobajic, Devansh Maheshwari, James Auck, Allison Rassmann, Ellis Powell, Morgane Malville, Craig Harris)

References:
https://www.supergiantgames.com/blog/hades-faq
https://gamerant.com/hades-developer-infographic-dialogue-breakdown/https://kotaku.com/the-secret-to-the-success-of-bastion-pyre-and-hades-1838082618
https://gamerant.com/hades-developer-supergiant-games-upcoming-projects-long-wait/
https://kotaku.com/the-secret-to-the-success-of-bastion-pyre-and-hades-1838082618