This project explores a journey through different emotional and cultural states, inspired by the progression of a day and the feeling of returning home. I structured the composition as four distinct sections: Subah (morning), Dhak (rhythm and movement), Toofaan (chaos), and Ghar (homecoming). Each section combines both audio and visuals to reflect a specific mood — from the softness of sunrise to the intensity of a storm, and finally to a calmer, resolved ending.

I spent a lot of time experimenting with how to connect TidalCycles and Hydra through MIDI so that the visuals would respond meaningfully to the music. The hardest part was getting everything to sync and behave consistently took multiple attempts and debugging sessions. I also iterated a lot on the visuals, trying to move away from repetitive patterns and instead create distinct visual identities for each section. The final result is something that feels cohesive but still varied, where both the audio and visuals evolve together as the piece progresses.

Tidal code

setcps (85/60/4)

silence_all = do
  d1 silence
  d2 silence
  d3 silence
  d4 silence
  d5 silence
  d6 silence
  d7 silence
  d8 silence
  d9 silence

subah = do
  -- scene select: 0 = SUBAH
  d1 $ n "0*8" # s "midi" # midichan 0 # ccn 0 # ccv "0"


  d2 $ n "0*16" # s "midi" # midichan 0 # ccn 1 # ccv (range 0 45 $ slow 12 sine)
  d8 $ n "0*16" # s "midi" # midichan 0 # ccn 2 # ccv (range 10 50 $ slow 16 sine)

  d3 $ slow 4 $ s "sitar" # n "<0 5 3 7>"
    # room 0.95 # gain 0.75 # speed 0.8
    # lpf 2200 # pan (slow 5 sine) # cut 1

  d4 $ slow 4 $ s "supersaw"
    # note "c4 e4"
    # sustain 4 # gain 0.35 # lpf 600 # room 0.9 # lpq 0.12

  d5 $ struct "t(3,16)" $ slow 2 $ s "tabla2"
    # n (irand 46) # room 0.85 # gain 0.6
    # speed (range 0.85 1.15 rand)

  d6 silence
  d7 silence


dhak = do
  d1 $ n "1*8" # s "midi" # midichan 0 # ccn 0 # ccv "1"

  d9 $ n "0*4" # s "midi" # midichan 0 # ccn 3 # ccv "<0 64 0 64>"

  -- hydra motion
  d2 $ n "0*16" # s "midi" # midichan 0 # ccn 1 # ccv (range 20 85 $ slow 4 saw)
  d8 $ n "0*16" # s "midi" # midichan 0 # ccn 2 # ccv (range 20 90 $ slow 8 sine)

  d3 $ stack [
    s "tabla2*8"
      # n "<[20 8 8 20 20 8 3 12] [20 8 3 20 20 3 8 12]>"
      # room 0.5 # gain (range 0.95 1.1 rand),
    s "tabla:0(3,8)" # room 0.4 # gain 1.05 # speed 0.8,
    s "~ cp" # room 0.6 # gain 0.9
    ]

  d4 $ sometimesBy 0.25 (# speed 2)
    $ every 4 rev
    $ s "pluck"
    # note "<c5 e5 g5 e5 a5 g5 e5 d5>"
    # room 0.55 # gain 0.85 # speed 1.5

  d5 $ slow 2 $ s "supersaw"
    # note "<c3 e3 c3 d3>"
    # sustain 1.5 # gain 0.4 # lpf 550 # room 0.75

  d6 $ struct "t(1,8)" $ s "sitar"
    # n (irand 8) # room 0.65 # gain 0.7 # cut 1

  d7 silence


toofaan = do
  d1 $ n "2*8" # s "midi" # midichan 0 # ccn 0 # ccv "2"

  d9 $ n "0*8" # s "midi" # midichan 0 # ccn 3 # ccv "<0 64 100 64 0 100 64 0>"

  d2 $ n "0*16" # s "midi" # midichan 0 # ccn 1 # ccv (range 45 127 $ slow 2 saw)
  d8 $ n "0*16" # s "midi" # midichan 0 # ccn 2 # ccv (range 30 127 $ slow 4 saw)

  d3 $ s "[808bd:4(5,8), ~ 808:3, ~ cp ~ cp]"
    # room 0.6 # krush 7
    # speed (slow 4 "<1.5 1>") # gain 1.15

  d4 $ rotL 1 $ every 4 (fast 2)
    $ s "tabla2*8"
    # n "<[20 6 6 20 8 6 3 12] [20 3 6 20 8 12 6 3]>"
    # room 0.3 # gain 1.05
    # speed (range 0.95 1.15 rand)

  d5 $ fast 2 $ s "supersaw"
    # note "<c5 e5 g5 a5 g5 e5 d5 c5>"
    # sustain 0.12 # gain 0.5 # room 0.35
    # lpf (slow 4 $ range 1000 7000 saw) # lpq 0.25

  d6 $ slow 2 $ s "moog:2"
    # note "<c4 d#4 e4 c4>"
    # legato 1 # gain 0.6
    # lpf (segment 1000 $ slow 4 $ range 200 2200 saw) # lpq 0.3

  d7 $ fast 2 $ s "hh*2 hh*2 hh*2 <hh*6 [hh*2]!3>"
    # room 0.4 # gain (range 0.9 1.1 rand)


    ghar = do
      d1 $ n "3*8" # s "midi" # midichan 0 # ccn 0 # ccv "3"

      d9 $ n "0*4" # s "midi" # midichan 0 # ccn 3 # ccv "<0 64 0 64>"

      d2 $ n "0*16"
        # s "midi"
        # midichan 0
        # ccn 1
        # ccv (range 28 4 $ slow 16 sine)

      d8 $ n "0*16"
        # s "midi"
        # midichan 0
        # ccn 2
        # ccv (range 30 6 $ slow 16 sine)

      d3 $ slow 2 $ s "sitar"
        # n "<0 2 4 7 7 4 2 0>"
        # gain 0.9
        # room 0.95
        # speed 0.82
        # lpf 1800
        # cut 1

      d4 $ slow 4 $ s "pluck"
        # n "<4 ~ 2 ~ 0 ~ -2 ~>"
        # gain 0.38
        # room 0.9
        # sustain 0.4
        # lpf 1500

      d5 $ slow 4 $ s "supersaw"
        # note "c4 e4"
        # sustain 5
        # gain 0.16
        # room 0.98
        # lpf 320
        # lpq 0.15

      d6 $ struct "t(2,16)" $ s "tabla2"
        # n "<20 8>"
        # gain 0.38
        # room 0.8
        # lpf 1400

      d7 $ struct "t(1,16)" $ s "pluck"
        # n "<7 4>"
        # gain 0.14
        # room 0.95
        # sustain 0.25
        # lpf 2200


        
 subah
dhak
toofaan
ghar


hush

hydra code

window.cc = Array(128).fill(0)
window.ccActual = Array(128).fill(0)

navigator.requestMIDIAccess().then(function(access) {
  var inputs = Array.from(access.inputs.values())
  var input = inputs.find(i => i.name && i.name.indexOf("IAC") !== -1) || inputs[0]

  if (!input) { window.hydraText = "No MIDI input found"; return }
  window.hydraText = "MIDI: " + input.name

  input.onmidimessage = function(msg) {
    var status = msg.data[0]
    var ctrl   = msg.data[1]
    var val    = msg.data[2]
    window.hydraText = "cc" + ctrl + " → " + val
    if (status === 176) {
      cc[ctrl] = val / 127
      if (ctrl === 0) ccActual[0] = Math.min(3, Math.floor(val / 32))
      if (ctrl === 3) ccActual[3] = Math.min(2, Math.floor(val / 43))
    }
  }
})

window.DD = 0.008
window.c = (u, i, j) => {
  let segs = u.split("/"), cs = segs[segs.length - 1].split("-")
  return parseInt("0x" + cs[i].substring(2 * j, 2 + 2 * j)) / 255
}
window.b = (o, u, i, y, z) =>
  o().add(solid(1,1,1), DD)
    .thresh(i * 0.2 * (z - y) + y, 0.0001)
    .luma(0.5, 0.0001)
    .color(c(u,i,0), c(u,i,1), c(u,i,2))

window.colorize = (x, u, y=0, z=1) =>
  b(x,u,0,y,z).layer(b(x,u,1,y,z)).layer(b(x,u,2,y,z))
               .layer(b(x,u,3,y,z)).layer(b(x,u,4,y,z))

window.asp = () => innerHeight / innerWidth

window.madhubaniP = 'https://coolors.co/1b3fa6-c0341f-e8b832-2d6e2d-1a1a1a'
window.pichwaiP   = 'https://coolors.co/0d5c63-e87ca0-f0c040-fdf6e3-1b4f8a'
window.mughalP    = 'https://coolors.co/1b3d8f-c0251a-d4a017-f5f0e8-4a2c10'
window.toofaanP   = 'https://coolors.co/e0105f-ff6b00-ffe000-00d4aa-7b00e0'
window.gharP      = 'https://coolors.co/c0522a-d4803a-e8b060-f5dfc0-3d2b1f'

hush()

window.subah_A = () => {
  let hatch = () =>
    osc(60, 0.008, 0.5)
      .rotate(Math.PI / 4)
      .diff(osc(60, 0.008, 0.5).rotate(-Math.PI / 4))
      .kaleid(4)
      .scale(1, asp)

  let fishBody = () =>
    voronoi(6, 0.25, 0.1)
      .diff(voronoi(12, 0.2, 0.08))
      .kaleid(4)
      .rotate(0, () => 0.003 + cc[1] * 0.005)
      .scale(1, asp)

  colorize(() => fishBody(), madhubaniP, 0, 1)
    .layer(
      colorize(() => hatch(), madhubaniP, 0.2, 0.7)
        .luma(() => 0.5 - cc[1] * 0.1, 0.005)
    )
    .modulate(
      noise(() => cc[1] * 1.2 + 0.4, 0.015),
      () => cc[1] * 0.025 + 0.005
    )
    .saturate(() => 2.0 + cc[2] * 1.0)
    .contrast(() => 1.4 + cc[2] * 0.4)
    .hue(() => cc[2] * 0.08)
    .brightness(() => cc[1] * 0.04 - 0.01)
    .out(o0)
}

window.subah_B = () => {
  let petal = () =>
    osc(20, 0.02, 1.2)
      .mult(osc(20, 0.02, 1.2).rotate(Math.PI / 8))
      .kaleid(8)
      .rotate(0, () => 0.002 + cc[1] * 0.004)
      .scale(0.9, asp)

  let outerBuds = () =>
    osc(40, 0.01, 0.8)
      .mult(osc(40, 0.01, 0.8).rotate(Math.PI / 16))
      .kaleid(16)
      .rotate(0, () => -0.001 - cc[1] * 0.002)
      .scale(1, asp)

  colorize(() => petal(), pichwaiP, 0, 1).out(o1)
  colorize(() => outerBuds(), pichwaiP, 0.15, 0.85).out(o2)

  src(o1)
    .layer(src(o2).luma(0.4, 0.01))
    .add(src(o0).scale(0.998).brightness(-0.015), () => 0.08 + cc[2] * 0.12)
    .hue(() => cc[2] * 0.05 + time * 0.002)
    .saturate(() => 1.6 + cc[2] * 0.8)
    .contrast(() => 1.2 + cc[1] * 0.2)
    .out(o0)
}

window.dhak_A = () => {
  let stars = () =>
    osc(22, 0.04, 1.4)
      .rotate(Math.PI / 6, () => 0.01 + cc[1] * 0.02)
      .mult(osc(22, 0.04, 1.4).rotate(-Math.PI / 6, () => -0.01 - cc[1] * 0.015))
      .diff(osc(44, 0.02, 0.6).rotate(Math.PI / 12))
      .kaleid(12)
      .scale(1, asp)

  let inlay = () =>
    osc(66, 0.01, 0.4)
      .rotate(Math.PI / 4, () => 0.005 + cc[1] * 0.008)
      .kaleid(12)
      .scale(1.05, asp)

  colorize(() => stars(), mughalP, 0, 1)
    .layer(
      colorize(() => inlay(), mughalP, 0.3, 0.9)
        .luma(() => 0.45 + cc[2] * 0.15, 0.005)
    )
    .modulateRotate(
      osc(4, 0.02, 0),
      () => cc[1] * 0.3 + 0.02
    )
    .hue(() => time * 0.004 + cc[2] * 0.06)
    .saturate(() => 1.8 + cc[2] * 0.7)
    .contrast(() => 1.35 + cc[1] * 0.35)
    .out(o0)
}

window.dhak_B = () => {
  let vine = () =>
    noise(() => cc[1] * 2 + 1.5, 0.025)
      .mult(
        osc(12, 0.03, 1)
          .rotate(0, () => 0.008 + cc[1] * 0.01)
          .kaleid(6)
      )
      .scale(1, asp)

  let veins = () =>
    osc(50, 0.006, 0.6)
      .rotate(Math.PI / 3)
      .diff(osc(50, 0.006, 0.6).rotate(-Math.PI / 3))
      .kaleid(6)
      .rotate(0, () => 0.004 + cc[1] * 0.006)
      .scale(1, asp)

  colorize(() => vine(), madhubaniP, 0, 1)
    .layer(
      colorize(() => veins(), madhubaniP, 0.25, 0.75)
        .luma(() => 0.4 + cc[2] * 0.2, 0.005)
    )
    .modulate(noise(2, 0.02), () => cc[1] * 0.03 + 0.008)
    .hue(() => cc[2] * 0.1 + time * 0.003)
    .saturate(() => 2.2 + cc[2] * 0.8)
    .contrast(() => 1.3 + cc[1] * 0.3)
    .brightness(() => cc[1] * 0.05 - 0.02)
    .out(o0)
}

window.toofaan_A = () => {
  let yantra = () =>
    osc(28, 0.04, 2)
      .rotate(0, () => 0.025 + cc[1] * 0.04)
      .diff(
        osc(28, 0.04, 2)
          .rotate(Math.PI / 3, () => -0.025 - cc[1] * 0.03)
      )
      .kaleid(() => Math.floor(cc[1] * 10 + 3))
      .scale(1, asp)

  src(o0)
    .layer(
      colorize(() => yantra(), toofaanP, 0, 1)
        .luma(() => 0.3 - cc[1] * 0.1, 0.01)
    )
    .modulate(
      noise(() => cc[1] * 5 + 2, 0.04),
      () => cc[1] * 0.07 + 0.005
    )
    .scale(0.996)
    .rotate(0, () => cc[1] * 0.004)
    .colorama(() => cc[2] * 0.1 + 0.004)
    .hue(() => cc[2] * 0.03 + time * 0.006)
    .saturate(() => 1.8 + cc[2] * 1.2)
    .contrast(() => 1.3 + cc[1] * 0.4)
    .out(o0)
}

window.toofaan_B = () => {
  let powder = () =>
    noise(() => cc[1] * 10 + 4, 0.08)
      .mult(osc(40, 0.03, 2).kaleid(8))
      .pixelate(() => 8 + cc[1] * 300, () => 8 + cc[1] * 300)
      .scale(1, asp)

  let arcs = () =>
    osc(55, 0.025, 1.8)
      .rotate(0, () => 0.03 + cc[1] * 0.05)
      .kaleid(6)
      .pixelate(() => 32 + cc[2] * 150, 32)
      .scale(1, asp)

  colorize(() => powder(), toofaanP, 0, 1)
    .layer(
      colorize(() => arcs(), toofaanP, 0.1, 0.9)
        .luma(() => 0.4 - cc[1] * 0.15, 0.01)
    )
    .colorama(() => cc[2] * 0.15 + 0.005)
    .hue(() => cc[2] * 0.05 + time * 0.008)
    .saturate(() => 2.0 + cc[1] * 0.8)
    .contrast(1.25)
    .out(o0)
}

window.toofaan_C = () => {
  let tiles = () =>
    osc(32, 0.05, 1.6)
      .rotate(Math.PI / 8, () => 0.02 + cc[1] * 0.04)
      .mult(osc(32, 0.05, 1.6).rotate(-Math.PI / 8, () => -0.015 - cc[1] * 0.03))
      .kaleid(8)
      .scale(1, asp)

  src(o0)
    .layer(
      colorize(() => tiles(), mughalP, 0, 1)
        .luma(() => 0.35 - cc[1] * 0.1, 0.01)
    )
    .modulatePixelate(
      noise(() => cc[1] * 6 + 2, 0.05).pixelate(16, 16),
      () => cc[1] * 768 + 8
    )
    .scale(() => 0.994 + cc[1] * 0.006)
    .rotate(0, () => cc[1] * 0.005)
    .colorama(() => cc[2] * 0.06 + 0.002)
    .hue(() => time * 0.005 + cc[2] * 0.04)
    .saturate(() => 1.6 + cc[2] * 0.8)
    .contrast(() => 1.2 + cc[1] * 0.3)
    .out(o0)
}

window.ghar_A = () => {
  let petal = () =>
    osc(14, 0.015, 1.0)
      .mult(osc(14, 0.015, 1.0).rotate(Math.PI / 8))
      .kaleid(8)
      .rotate(0, () => 0.001 + cc[1] * 0.002)
      .scale(0.85, asp)

  let water = () =>
    voronoi(3, 0.05, 0.3)
      .diff(voronoi(6, 0.04, 0.25))
      .scrollY(() => time * 0.003)
      .scale(1, asp)

  colorize(() => water(), pichwaiP, 0, 0.8)
    .layer(
      colorize(() => petal(), pichwaiP, 0.1, 0.75)
        .luma(() => 0.25 + cc[1] * 0.15, 0.02)
    )
    .add(
      src(o0).scale(0.999).brightness(-0.025),
      () => 0.15 + cc[2] * 0.12
    )
    .hue(() => cc[2] * 0.04 + 0.001)
    .saturate(() => 1.2 + cc[2] * 0.5)
    .brightness(() => -0.04 + cc[1] * 0.04)
    .contrast(1.1)
    .out(o0)
}

window.ghar_B = () => {
  let warli = () =>
    shape(3, 0.55, 0.01)
      .scale(1, asp)
      .kaleid(6)
      .rotate(0, () => 0.003 + cc[1] * 0.005)
      .mult(
        osc(8, 0.015, 0.5)
          .kaleid(6)
          .rotate(Math.PI / 6, () => -0.002 - cc[1] * 0.003)
      )

  let alpana = () =>
    voronoi(4, 0.08, 0.18)
      .diff(voronoi(9, 0.07, 0.14))
      .scrollY(() => time * 0.004)
      .scale(1, asp)

  colorize(() => alpana(), gharP, 0, 1)
    .layer(
      colorize(() => warli(), gharP, 0.1, 0.7)
        .luma(() => 0.22 + cc[1] * 0.18, 0.02)
    )
    .modulateRotate(noise(1.5, 0.012), () => cc[1] * 0.035 + 0.003)
    .add(src(o0).scale(0.999).brightness(-0.018), () => 0.1 + cc[2] * 0.1)
    .hue(() => cc[2] * 0.05 + 0.001)
    .saturate(() => 1.1 + cc[2] * 0.4)
    .brightness(() => -0.03 + cc[1] * 0.04)
    .contrast(1.12)
    .out(o0)
}

window.launchSubah = () => {
  hush()
  var v = ccActual[3] || 0
  if (v === 0) subah_A()
  else         subah_B()
}

window.launchDhak = () => {
  hush()
  var v = ccActual[3] || 0
  if (v === 0) dhak_A()
  else         dhak_B()
}

window.launchToofaan = () => {
  hush()
  var v = ccActual[3] || 0
  if      (v === 0) toofaan_A()
  else if (v === 1) toofaan_B()
  else              toofaan_C()
}

window.launchGhar = () => {
  hush()
  var v = ccActual[3] || 0
  if (v === 0) ghar_A()
  else         ghar_B()
}

window.whichScene  = 0
window.whichSub    = 0

launchSubah()

update = () => {
  var nextScene = Math.max(0, Math.min(3, ccActual[0]))
  var nextSub   = Math.max(0, Math.min(2, ccActual[3]))

  if (window.whichScene !== nextScene || window.whichSub !== nextSub) {
    window.whichScene = nextScene
    window.whichSub   = nextSub
    switch (nextScene) {
      case 0: launchSubah();   break
      case 1: launchDhak();    break
      case 2: launchToofaan(); break
      case 3: launchGhar();    break
    }
  }
}