Click and/or drag to morph. X-axis changes square wave frequency, Y-axis changes triangle wave amplitude. F or 1-finger touch for fullscreen, S to save an image, P, space, center mouse button, or 2-finger touch to pause animation, L or 3-finger touch to toggle lines.
xxxxxxxxxx
/*
7-26-2023
Based on a cool gif by Yann Le Gall, https://genart.social/@ylegall/110657251187118294
I had suggested it yesterday for a fun challenge for SableRaph's Twitch stream to recreate it based on a description of it
after describing it in so much detail and seeing SableRaph's nice interpretation, https://editor.p5js.org/SableRaf/full/3mzBW6qA1
I decided I had to give it a go. I was able to make a proper modulated square wave in only a half hour,
but it was impossible to make it animated with that structure. Many hours later, I had the proper structure,
and at that point, I decided to go all out with it and make it my own.
*/
function setup(reset=true) {
S = min(windowWidth, windowHeight)
s = S/2
createCanvas(W = windowWidth, H = windowHeight);
noFill()
t = 0
P = max(W,H)/4
if(reset){
mX = 1
mY = 1
looping = true
}
lines_x_inc_min = W/150
lines_toggle = true
frameRate(60)
}
function draw() {
if(mouseIsPressed){
mX = lerp(mX, max(0.2, mouseX/W * 5), 0.05)
mY = lerp(mY, max(0.2, mouseY/H * 3), 0.05)
}
x_inc = W/60 * mX
const lines_bool = lines_toggle && x_inc > lines_x_inc_min
const wt = x_inc * 0.65
const wt2 = wt/3
A = max(wt*2, S/8 * mY)
strokeWeight(wt)
background(30)
if(looping) t += 1
const A2 = A - wt * 1.3
let sign3 = 1
let y_pos = 0
let y_count = 0
while(y_pos< H + A){
let sign2 = 1
push()
translate(0,y_pos)
y_pos += A
let x = - x_inc
stroke(80 + sign2 * 70)
const phase_offset = (y_count % 2 == 0) * (P/2) + t
let y2 = tri_wave(x, A2, phase_offset)
sign3 *= -1
y_count++
const y_count_factor = y_count * A/H * 50 - 20
let x_count = 0
while(x <= W + x_inc){
x_ratio = x/W
const A_mod = sign3 * tri_wave(x, A2, phase_offset)
sign2 *= -1
// let jitter = x_inc * noise(A_mod/100) // an experiment that I chose not to use
// let x2 = x - jitter
const A_mod_opp = A_mod * sign2
const A_ratio = abs(A_mod/A)
stroke(150 - 80 * sign3 * A_ratio + sign2 * 70 + y_count_factor,
x_ratio * 50 * sign3 + 100 * (1 - sign3) - y_count_factor,
y_count_factor - x_ratio * 50 + 150 * sign3,
230 - 170 * ((sign3 > 0)? A_ratio : 1-A_ratio))
line(x,A_mod_opp,x,y2)
y2 = A_mod_opp
line(x,y2,x + x_inc,A_mod_opp)
if(lines_bool){
push()
strokeWeight(wt2)
let A_mod2 = sign3 * tri_wave(x+x_inc*2, A2, phase_offset) // draw angled lines. Kinda cool
if(x_count % 2 == 1) line(x,A_mod,x + x_inc*2,A_mod2)
else line(x,-A_mod,x + x_inc*2,-A_mod2)
pop()
}
x_count++
x += x_inc
}
pop()
}
if(!looping && !mouseIsPressed ) noLoop()
}
function tri_wave(x, A, p){
return y = abs(-A/P * (P - 2*abs((x + p) % (2*P) - P)))
}
function mousePressed(){
const touchmode = touches.length
if(mouseButton === CENTER || touchmode === 2) loop_toggle()
else if(touchmode === 1){
go_fullscreen()
loop()
}
else if(touchmode === 3){
lines_toggle = !lines_toggle
loop()
}
else if(touchmode === 0 && mouseButton === LEFT) loop()
}
function keyPressed(){
if(key === 'p' || key === ' ') loop_toggle()
else if(key === 'r') setup(reset=true)
else if (key === 'f') {
if (fullscreen()) fullscreen(false)
else go_fullscreen()
loop()
}
else if (key === 's') save_img()
else if (key === 'l'){
lines_toggle = !lines_toggle
loop()
}
}
function loop_toggle(){
looping = !looping
if(looping) loop()
else noLoop()
}
function go_fullscreen() {
document.documentElement.webkitRequestFullScreen();
}
function windowResized() {
setup(reset=false)
if(!looping) loop()
}
function save_img() {
save_name = "Triangle-Square-Waves"
if(mX != 0.5 || mY != 0.5) save_name += '-' + nf(mX,0,2) + '-' + nf(mY,0,2)
save_name += '-' + int(W * pixelDensity()) + 'x' + int(H * pixelDensity())
saveCanvas(save_name, "png")
}
// document.addEventListener("contextmenu", (event) => event.preventDefault(), "true")
function preventBehavior(e) {
e.preventDefault()
}
document.addEventListener("touchmove", preventBehavior, {passive: false})