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