Reading about “the instrumental impulse” in this paper reminded me of the psychological concept of Perceptual Narrowing—a cognitive phenomenon where, for efficiency, humans develop schemas that help make fast decisions, but in doing so, lose access to a broader range of possibilities. When I was studying about surrealism, surrealists believed one way to return to authenticity was through Automatism—letting the mind wander freely, uncensored. This mirrors the spirit of improvisation, where intuition precedes logic and the unknown is welcomed. I believe this can be applied to live-coding, or, in broader concept, improvisation becomes a tool not just for performance, but for rediscovering what expression and creativity can mean beyond established norms.

I was especially moved by the author’s framing of the computer as a romantic, expressive instrument—not just a cold rational machine. Just like traditional instruments, laptops in live coding are personalized by each performer’s unique techniques and approaches. In this way, every computer becomes a partner, emotionally bonded with the artist. This echoes how traditional musicians develop intimate relationships with their instruments, shaping their identity over time.

Lastly, I found the contrast between live coding and tools like Ableton or Radial insightful. While those tools prioritize accessibility and user-friendly design, live coding embraces the rawness of syntax and structural logic. From my perspective, neither is better or worse—just different in intention. Live coding reflects the artists’ passion for exploring the pure beauty of machine, representing a more experimental, deeply embodied relationship with technology—one that reimagines both the machine and the human in the act of creation.

Inspired by a p5.js sketch I had created during Intro to IM:

P5 Code:

let baseSize = 40;
let p5 = new P5();
s0.init({ src: p5.canvas });
p5.hide();
p5.frameRate(30);

p5.setup = () => {
  p5.createCanvas(400, 400);
};

p5.draw = () => {
  p5.background(220, 220, 220, 40);

  let cc0 = (typeof cc !== 'undefined' && cc[0] != null) ? cc[0] : 0;
  let cc1 = (typeof cc !== 'undefined' && cc[1] != null) ? cc[1] : 0;

  // determine mode:
  // Mode 0: Grid of ellipses (cc0 < 0.33)
  // Mode 1: Rotating lines (0.33 ≤ cc0 < 0.66)
  // Mode 2: Central blob (cc0 ≥ 0.66)
  let mode = cc0 < 0.33 ? 0 : cc0 < 0.66 ? 1 : 2;

  // map cc1 to color values
  let r = p5.map(cc1, 0, 1, 50, 255);
  let g = p5.map(cc1, 0, 1, 100, 200);
  let b = p5.map(cc1, 0, 1, 150, 255);

  p5.noStroke();

  if (mode === 0) {
    // mode 0: Grid of ellipses
    let spacing = p5.map(p5.mouseX, 0, p5.width, 20, 50);
    let size = baseSize * (1 + cc1) + p5.map(p5.mouseY, 0, p5.height, 0, 20);
    for (let x = 0; x < p5.width; x += spacing) {
      for (let y = 0; y < p5.height; y += spacing) {
        p5.fill(
          p5.random(r - 20, r + 20),
          p5.random(g - 20, g + 20),
          p5.random(b - 20, b + 20),
          120
        );
        p5.ellipse(x, y, size, size);
      }
    }
  } else if (mode === 1) {
    // mode 1: Rotating lines
    p5.push();
    p5.translate(p5.width / 2, p5.height / 2);
    let numLines = 12 + Math.floor(cc1 * 12) + Math.floor(p5.map(p5.mouseX, 0, p5.width, 0, 6));
    let step = p5.TWO_PI / numLines;
    let speed = 0.02 + p5.map(p5.mouseY, 0, p5.height, 0, 0.05);
    for (let i = 0; i < numLines; i++) {
      let angle = i * step + p5.frameCount * speed;
      let len = 100 + cc1 * 100;
      p5.stroke(r, g, b, 150);
      p5.strokeWeight(2);
      p5.line(0, 0, len * p5.cos(angle), len * p5.sin(angle));
    }
    p5.pop();
  } else if (mode === 2) {
    // mode 2: Central blob multiple rotated polygons
    p5.push();
    p5.translate(p5.width / 2, p5.height / 2);
    let copies = 6 + Math.floor(cc1 * 6) + Math.floor(p5.map(p5.mouseX, 0, p5.width, 0, 4));
    let step = p5.TWO_PI / copies;
    for (let i = 0; i < copies; i++) {
      p5.push();
      p5.rotate(i * step + p5.frameCount * 0.01 + p5.map(p5.mouseY, 0, p5.height, 0, 0.05));
      p5.fill(r, g, b, 180);
      let sides = 5 + Math.floor(cc1 * 5) + Math.floor(p5.map(p5.mouseY, 0, p5.height, 0, 3));
      let radius = 50 + cc1 * 50;
      p5.beginShape();
      for (let j = 0; j < sides; j++) {
        let a = p5.map(j, 0, sides, 0, p5.TWO_PI);
        p5.vertex(radius * p5.cos(a), radius * p5.sin(a));
      }
      p5.endShape(p5.CLOSE);
      p5.pop();
    }
    p5.pop();
  }
};

src(s0)
  .modulate(noise(3, 0.2), 0.05)
  .blend(osc(10, 0, 1).rotate(0.1), 0.1)
  .out(o0);

render(o0);

// hush()

Tidal:


d1 $ slow 2 $ s "house(4,8)" # gain 0.9

d2 $ stack [s "drum(4,8) realclaps(4,8)" # speed 4 ,ccv "<64 0 127 0>" # ccn "0" # s "midi"]

d3 $ stack [ s "popkick(8,16)" # speed 3 , ccv "<32 96 127 64>" # ccn "1" # s "midi" ]

d5 $ stack [ s "pad(4,8)" # speed 1.5, ccv "<127 64 32>" # ccn "2" # s "midi" ]

d5 silence

Output (please disregard the audio quality, my microphone was not working properly):

Hydra Code:

let p5 = new P5();
p5.createCanvas(p5.windowWidth - 100, p5.windowHeight - 100, p5.WEBGL, p5.canvas);
s0.init({src: p5.canvas});
src(s0).out();
p5.hide();

p5.angleMode(p5.DEGREES);
p5.colorMode(p5.HSB);
p5.stroke(199, 80, 88);
p5.strokeWeight(3);
p5.noFill();
p5.pixelDensity(1);

let r = p5.width / 3;
let rotationSpeed = 0.8;
let hueShift = 0;

const settings = [0.75, 5, 5];

p5.draw = () => {
  p5.clear();
  p5.orbitControl();
  p5.rotateX(p5.frameCount * rotationSpeed);
  p5.rotateY(p5.frameCount * rotationSpeed);

  settings[0] = cc[0] || 0.75;
  settings[1] = cc[1] ? cc[1] * 10 : 5;
  settings[2] = cc[2] ? cc[2] * 10 : 5;

  p5.rotateX(65);
  p5.beginShape(p5.POINTS);
  for (let theta = 0; theta < 180; theta += 2) {
    for (let phy = 0; phy < 360; phy += 2) {
      let x = r * (1 + settings[0] * p5.sin(settings[1] * theta) * p5.sin(settings[2] * phy))
              * p5.sin(theta) * p5.cos(phy);
      let y = r * (1 + settings[0] * p5.sin(settings[1] * theta) * p5.sin(settings[2] * phy))
              * p5.sin(theta) * p5.sin(phy);
      let z = r * (1 + settings[0] * p5.sin(settings[1] * theta) * p5.sin(settings[2] * phy))
              * p5.cos(theta);
      p5.stroke((hueShift + theta + phy) % 360, 80, 88);
      p5.vertex(x, y, z);
    }
  }
  p5.endShape();

  hueShift += 0.1;
}

src(s0)
  .modulatePixelate(noise(()=>cc[2]*20,()=>cc[0]),20)
  .out()

src(s0).modulate(noise(4, 1.5), 0.6).mult(osc(1, 10, 1)).out(o2)

render(o2)

src(o2)
  .modulate(src(o1).add(solid(0, 0), -0.5), 0.005)
  .blend(src(o0).add(o0).add(o0).add(o0), 0.1)
  .out(o2)

render(o2)


hush()

TidalCycle Code:

d1 $ ccv "0 40 80 127" # ccn "0" # s "midi"

d2 $ slow 4 $ rotL 1 $ every 4 (# ccv (fast 2 (range 127 0 saw))) $ ccv (segment 128 (range 127 0 saw)) # ccn "1" # s "midi"

d3 $ slow 4 $ ccv (segment 128 (slow 4 (range 127 0 saw))) # ccn "2" # s "midi"

d4 $ slow 4 $ note "c'maj d'min a'min g'maj" # s "superpiano" # legato 1 # sustain 1.5

d5 $ slow 4 $ note "c3*4 d4*4 a3*8 g3*4" # s "superhammond:5"
  # legato 0.5

xfade 4 $ "bd <hh hh*2 hh*4> sd <hh [hh bd]>"
  # room 0.2

once $ s "auto:3"

hush

Link to the video.

Hydra Code:


let p5 = new P5()
s0.init({src: p5.canvas})
p5.hide()

// No need for setup
p5.noFill()
p5.strokeWeight(10)
p5.stroke(255)

// Shape positions
let shapePositions = [
  { x: p5.width / 4, y: p5.height / 2, size: 200 },
  { x: (p5.width / 4) * 3, y: p5.height / 2, size: 200 }
]

p5.draw = () => {
  p5.background(0)

  // First triangle
  p5.triangle(
    shapePositions[0].x, shapePositions[0].y - shapePositions[0].size / 2,
    shapePositions[0].x - shapePositions[0].size / 2, shapePositions[0].y + shapePositions[0].size / 2,
    shapePositions[0].x + shapePositions[0].size / 2, shapePositions[0].y + shapePositions[0].size / 2
  )

  // Second square
  p5.rectMode(p5.CENTER);
  p5.square(shapePositions[1].x, shapePositions[1].y, shapePositions[1].size);

  if (cc[1] == 1) {
    p5.triangle(
      p5.width / 2, p5.height / 2 - (400 * cc[0] + 200 * p5.noise(cc[0])) / 2,
      p5.width / 2 - (400 * cc[0] + 200 * p5.noise(cc[0])) / 2, p5.height / 2 + (400 * cc[0] + 200 * p5.noise(cc[0])) / 2,
      p5.width / 2 + (400 * cc[0] + 200 * p5.noise(cc[0])) / 2, p5.height / 2 + (400 * cc[0] + 200 * p5.noise(cc[0])) / 2
    )
  } else {
    p5.square(
      p5.noise(cc[0] * 2) * p5.width,
      cc[0] * p5.height,
      200
    )
  }
}


src(s0).modulate(osc(15, 0.3, 1.2), 0.1).mult(noise(2, 0.8)).diff(src(o1)).out();
src(s0).modulate(gradient().rotate(1.5), 0.5).mult(osc(8, 0.2, 2)).diff(src(o1)).out();
src(s0).modulate(noise(150, 1.2), 0.7).mult(osc(25, 5, 30)).diff(src(o1)).out();

// Feedback effects
src(s0).modulate(noise(4, 1.5), 0.6).mult(osc(1, 5, 1)).out(o2);
src(o2)
  .modulate(src(o1).add(solid(0, 0), -0.5), 0.005)
  .blend(src(o0).add(o0).add(o0).add(o0), 0.1)
  .out(o2)

render(o2)


hush()

Tidal Code:

-- Sound
d1 $ s "bleep" # room 0.6 # gain 1
d2 $ s "~cp" # gain 1.2 # room 0.3

d3 $ sometimes (# velocity 0.6) $ iter 4 $ struct "<t(4,8) t(4,8,1)>" $ s "cp"

d4 $ s "superpiano" >| note (scale "major" ("[1 3 2 8 9 6]") + "8") # room 0.4 #gain 2 

-- CC
d5 $ whenmod 32 12 (# ccn ("<t(4,8) t t(4,8,1) t>"))
    $ ccn "0*64" # ccv (slow 3 (range 10 127 tri))
    # s "midi"

d6 $ whenmod 32 12 (# ccv "127") $ ccn "1*128" # ccv 0 # s "midi"





hush

Also Hi,

Apologies for uploading this late. Laptop was not cooperating.

Originally, I had gone for one of the visuals from tgh wesbite that was shared with us. However, Pulsar afterwards, crashed towards the end so I decided to use a simple visual I had made during my Intro to IM class. It’s a little over 2 mins (sorry :/ )

https://youtu.be/XYRRBaNS35w

Here is my Tidal code!

d2 $ struct "<t(3,8) t(5,8)>" $ s "casio" # n (run 8)

d4 $ struct "<t(3,8) t(5,8)>" $ ccv "<169 109 120 127>" 
  # ccn "0" 
  # s "midi"


d1 $ n ("e4 d c a b a b c7" |+ "<2 2 7 12>") # s "[superpiano, cp, bd, arpy, bd]"  # room 1

d3 $ struct "<t(3,8) t(5,8)>" $ ccv "<127 115 66 107>" 
  # ccn "0" 
  # s "midi"

d2 silence


hush

Hydra:

let p5 = new P5(); 
s0.init({ src: p5.canvas }); 
src(s0).out(); 

p5.hide(); 

let bubbles = [];

p5.draw = () => {
  if (bubbles.length === 0) {
    p5.createCanvas(window.innerWidth, window.innerHeight);
    for (let i = 0; i < 30; i++) {
      bubbles.push(new Bubble(p5.random(p5.width), p5.random(p5.height), p5.random(20, 100)));
    }
  }
  
  p5.background(137, 207, 240, 50);

  for (let i = 0; i < bubbles.length; i++) {
    bubbles[i].move();
    bubbles[i].display();
  }

  if (p5.frameCount % 15 === 0) {
    bubbles.push(new Bubble(p5.random(p5.width), p5.height, p5.random(20, 100)));
  }
};

class Bubble {
  constructor(x, y, r) {
    this.x = x;
    this.y = y;
    this.r = r;
    this.speed = p5.map(this.r, 20, 100, 2, 0.5);
    this.color = p5.color(p5.random(100, 255), p5.random(100, 255), p5.random(255), p5.random(100, 200));
  }

  move() {
    this.y -= this.speed;
    this.x += p5.random(-1, 1);
  }

  display() {
    p5.fill(this.color);
    p5.noStroke();
    p5.ellipse(this.x, this.y, this.r);
  }
}

src(s0)
    .mult(osc(2, () => cc[0] * 2, 3))
    .modulate(noise(() => cc[1] * 0.5))  
    .rotate( () => cc[0] * 0.5 )        
    .colorama(() => cc[0] * 1)       
    .out();

src(o2)
  .modulate(src(o1)
  .modulate(noise(() => cc[1] * 0.05))  
  .rotate( () => cc[2] * 0.2 ))
  .colorama(() => cc[0] * 2)       
  .blend(src(o0))
  .out(o2)

  
render(o2)

hush()

p5.js/Hydra code:

let p5 = new P5()
s0.init({src: p5.canvas})
p5.hide();

let particles = [];
let numParticles = 50;
let mode = 2;

for (let i = 0; i < numParticles; i++) {
  particles.push({
    x: p5.random(p5.width),
    y: p5.random(p5.height),
    size: p5.random(5, 20),
    speedX: p5.random(-2, 2),
    speedY: p5.random(-2, 2),
    color: p5.color(p5.random(255), p5.random(255), p5.random(255))
  });
}

p5.draw = () => {
  p5.background(0); 

  if (ccActual[0] < 20) {
    mode = 0; // Particles mode
  } else if (ccActual[0] < 40) {
    mode = 1; // Spiral mode
  } else if (ccActual[0] < 60) {
    mode = 2; // Grid mode
  } else if (ccActual[0] < 80) {
    mode = 3; // Starburst mode
  } else if (ccActual[0] < 110) {
    mode = 4; // Waveform mode
  } else {
    mode = 5; // Fractal mode
  }

  // Draw based on current mode
  switch(mode) {
    case 0: // Particles
      drawParticles();
      break;
    case 1: // Spiral
      drawSpiral();
      break;
    case 2: // Grid
      drawGrid();
      break;
    case 3: // Starburst
      drawStarburst();
      break;
    case 4: // Waveform
      drawWaveform();
      break;
    case 5: // Fractal
      drawFractal();
      break;
  }
}

function drawParticles() {
  p5.noStroke();
  for (let i = 0; i < particles.length; i++) {
    let p = particles[i];
    p5.fill(p.color);
    p5.ellipse(p.x, p.y, p.size, p.size);

    p.x += p.speedX;
    p.y += p.speedY;

    if (p.x < 0 || p.x > p5.width) p.speedX *= -1;
    if (p.y < 0 || p.y > p5.height) p.speedY *= -1;
  }
}

function drawSpiral() {
  p5.stroke(255, 0, 100);
  p5.strokeWeight(3);
  p5.noFill();
  p5.translate(p5.width/2, p5.height/2);

  for (let i = 0; i < 200; i++) {
    let r = i * 0.5;
    let angle = i * 0.1 + time * 0.2;
    let x = r * p5.cos(angle);
    let y = r * p5.sin(angle);
    p5.point(x, y);
    if (i > 0) {
      let prevX = (i-1) * 0.5 * p5.cos((i-1) * 0.1 + time * 0.2);
      let prevY = (i-1) * 0.5 * p5.sin((i-1) * 0.1 + time * 0.2);
      p5.line(prevX, prevY, x, y);
    }
  }
}

function drawGrid() {
  p5.stroke(0, 255, 255);
  p5.strokeWeight(2);
  let cellSize = 50;

  for (let x = 0; x < p5.width; x += cellSize) {
    for (let y = 0; y < p5.height; y += cellSize) {
      let distFromCenter = p5.dist(x, y, p5.width/2, p5.height/2);
      let size = p5.map(p5.sin(distFromCenter * 0.01 + time), -1, 1, 10, cellSize-5);
      p5.rect(x, y, size, size);
    }
  }
}

function drawStarburst() {
  p5.stroke(255, 255, 0);
  p5.strokeWeight(2);
  p5.translate(p5.width/2, p5.height/2);

  for (let i = 0; i < 36; i++) {
    let angle = i * p5.TWO_PI / 36;
    let x1 = 100 * p5.cos(angle);
    let y1 = 100 * p5.sin(angle);
    let x2 = 300 * p5.cos(angle + time * 0.5);
    let y2 = 300 * p5.sin(angle + time * 0.5);
    p5.line(x1, y1, x2, y2);
  }
}

function drawWaveform() {
  p5.stroke(0, 255, 0);
  p5.strokeWeight(3);
  p5.noFill();
  p5.beginShape();

  for (let x = 0; x < p5.width; x += 10) {
    let y = p5.height/2 + p5.sin(x * 0.02 + time) * 100 +
            p5.cos(x * 0.01 - time * 0.5) * 50;
    p5.vertex(x, y);
  }

  p5.endShape();
}

function drawFractal() {
  p5.stroke(255);
  p5.noFill();
  p5.translate(p5.width/2, p5.height/2);
  drawBranch(100, 0, 8);
}

function drawBranch(len, angle, depth) {
  if (depth <= 0) return;

  p5.strokeWeight(depth);
  p5.stroke(255 - depth * 30, depth * 30, 150);

  p5.push();
  p5.rotate(angle);
  p5.line(0, 0, 0, -len);
  p5.translate(0, -len);

  let t = time * 0.5;
  drawBranch(len * 0.7, angle + p5.sin(t) * 0.5, depth - 1);
  drawBranch(len * 0.7, angle - p5.cos(t) * 0.5, depth - 1);
  p5.pop();
}
src(s0).modulate(noise(5,0.1),0.1).blend(osc(15,0.2,()=>ccActual[0]/127).hue(()=>ccActual[0]/20),0.3).out()

TidalCycles code:


d1 $ ccv (slow 4 "0 25 50 75 100 127")
  # ccn "0"
  # s "midi"

d2 $ stack [
  n (arp "<up down diverge>" (slow 4 $ "a'min7 c'maj7 e'min7 g'maj7"))
    # s "arpy"
    # gain (slow 4 $ range 0.5 0.9 $ "0 25 50 75 100 127" / 127)
    # room 0.3 # size 0.5,
  n (slow 4 $ "a2 c3 e3 g3 c4 e4")
    # s "jvbass"
    # lpf (slow 4 $ range 300 2000 $ "0 25 50 75 100 127")
    # gain 0.8,
    
  every 3 (fast 2) $ n (slow 2 $ scramble 8 $ run 8)
    # s "east"
    # gain 0.7
    # pan (slow 8 $ sine)
]

d3 $ slow 8 $ s "padlong"
    # gain 0.6
    # lpf (slow 4 $ range 500 5000 $ "0 25 50 75 100 127")
    # hpf 300

d4 $ every 4 (jux rev) $ whenmod 8 6 (fast 2) $
    n (slow 2 $ "0 [~ 1] 2 [3 4]")
    # s "feel"
    # gain 0.75
    # room 0.2
    # orbit 1

d5 $ every 2 (# gain 1.5) $
    s "glitch:5*8"
    # gain (slow 2 $ range 0 0.8 $ "0 25 50 75 100 127" / 127)
    # speed (range 0.5 1.5 $ slow 16 sine)
    # pan (slow 3 $ rand)
    # cut 1

d2 silence 
d3 silence
d5 silence
hush

Link to demo video (apologies for the keystrokes, my supercollider/pulsar has been very laggy and buggy and I was not able to record from supercollider so I had to rely on Quicktime recording)

Thank you!