For fun I asked Gemini to write me a JSFX. **Warning**: I've not tried any of these yet, I'm just dumping them here as I play around with prompts. ## 1 ```h A reaper JSFX effect with a single filter (cutoff and resonance, and possibly other parameters), with various models. ``` ```plaintext desc:Multi-Model State Variable Filter author:Gemini version:1.0 // --- SLIDERS --- slider1:1000<20,20000,1,log>Cutoff (Hz) slider2:0.707<0.1,10,0.01>Resonance (Q) slider3:0<0,3,1{Lowpass,Highpass,Bandpass,Notch}>Filter Model slider4:0<-12,12,0.1>Output Gain (dB) @init // Initialize variables ic1eq = 0; ic2eq = 0; @slider // Pre-calculate filter coefficients // We use the trapezoidal integration method for better response at high frequencies target_freq = slider1; q_val = slider2; model = slider3; amp = 10^(slider4/20); g = tan($pi * target_freq / srate); k = 1 / q_val; a1 = 1 / (1 + g * (g + k)); a2 = g * a1; a3 = g * a2; @sample // The SVF Algorithm // Inputs: spl0 (left), spl1 (right) // Note: This implementation is Mono for simplicity, applied to both channels. // Process Left Channel v0 = spl0; v3 = v0 - ic2eq; v1 = a1 * ic1eq + a2 * v3; v2 = ic2eq + a2 * ic1eq + a3 * v3; ic1eq = 2 * v1 - ic1eq; ic2eq = 2 * v2 - ic2eq; // Select Output based on 'model' slider model == 0 ? (out = v2); // Lowpass model == 1 ? (out = v0 - k*v1 - v2); // Highpass model == 2 ? (out = v1); // Bandpass model == 3 ? (out = v0 - k*v1); // Notch spl0 = out * amp; // Process Right Channel (Re-using logic for stereo) // Note: For true stereo, you'd want a second set of ic1eq/ic2eq variables. v0_r = spl1; v3_r = v0_r - ic2eq_r; v1_r = a1 * ic1eq_r + a2 * v3_r; v2_r = ic2eq_r + a2 * ic1eq_r + a3 * v3_r; ic1eq_r = 2 * v1_r - ic1eq_r; ic2eq_r = 2 * v2_r - ic2eq_r; model == 0 ? (out_r = v2_r) : model == 1 ? (out_r = v0_r - k*v1_r - v2_r) : model == 2 ? (out_r = v1_r) : model == 3 ? (out_r = v0_r - k*v1_r); spl1 = out_r * amp; ``` ## 2 ```h Yes please add drive. Could you also add a midi triggered envelope and lfo. ``` ```plaintext desc:Power Filter (Drive, LFO, & MIDI Envelope) author:Gemini version:1.1 slider1:1000<20,20000,1,log>Cutoff (Hz) slider2:0.707<0.1,10,0.01>Resonance (Q) slider3:0<0,3,1{Lowpass,Highpass,Bandpass,Notch}>Filter Model slider4:0<0,100,0.5>Drive (%) slider5:0<-100,100,1>LFO Amount (%) slider6:1<0.1,20,0.1>LFO Rate (Hz) slider7:5000<0,15000,1>Env Mod (Hz) slider8:10<1,1000,1>Env Attack (ms) slider9:100<1,2000,1>Env Release (ms) @init ic1eq = ic2eq = ic1eq_r = ic2eq_r = 0; // State variables lfo_phase = 0; env_val = 0; gate = 0; @slider drive_val = slider4 * 0.05; lfo_inc = 2 * $pi * slider6 / srate; // Convert ms to coefficients a_coeff = 1 - exp(-1 / (slider8 * srate / 1000)); r_coeff = 1 - exp(-1 / (slider9 * srate / 1000)); @block while (midirecv(offset, msg1, msg2, msg3)) ( status = msg1 & $F0; status == $x90 && msg3 > 0 ? gate = 1; // Note On status == $x80 || (status == $x90 && msg3 == 0) ? gate = 0; // Note Off midisend(offset, msg1, msg2, msg3); // Pass MIDI through ); @sample // 1. Update Envelope target = gate ? 1 : 0; env_val += (target - env_val) * (gate ? a_coeff : r_coeff); // 2. Update LFO lfo_phase += lfo_inc; lfo_phase > 2 * $pi ? lfo_phase -= 2 * $pi; lfo_sig = sin(lfo_phase); // 3. Combine Modulators for Cutoff mod_cutoff = slider1 + (lfo_sig * slider5 * 20) + (env_val * slider7); mod_cutoff = max(20, min(mod_cutoff, 20000)); // Safety Clamp // 4. SVF Math with Drive g = tan($pi * mod_cutoff / srate); k = 1 / slider2; a1 = 1 / (1 + g * (g + k)); a2 = g * a1; a3 = g * a2; // --- Left Channel --- v0 = spl0; v3 = v0 - ic2eq; v1 = a1 * ic1eq + a2 * v3; v2 = ic2eq + a2 * ic1eq + a3 * v3; // Soft clipping 'Drive' inside the feedback loop v1 = tanh(v1 * (1 + drive_val)); ic1eq = 2 * v1 - ic1eq; ic2eq = 2 * v2 - ic2eq; // --- Right Channel --- v0_r = spl1; v3_r = v0_r - ic2eq_r; v1_r = a1 * ic1eq_r + a2 * v3_r; v2_r = ic2eq_r + a2 * ic1eq_r + a3 * v3_r; v1_r = tanh(v1_r * (1 + drive_val)); ic1eq_r = 2 * v1_r - ic1eq_r; ic2eq_r = 2 * v2_r - ic2eq_r; // Select Output model = slider3; model == 0 ? (spl0 = v2; spl1 = v2_r) : model == 1 ? (spl0 = v0 - k*v1 - v2; spl1 = v0_r - k*v1_r - v2_r) : model == 2 ? (spl0 = v1; spl1 = v1_r) : (spl0 = v0 - k*v1; spl1 = v0_r - k*v1_r); ``` ## 3 ```h Yes please add a visualiser. ``` ```plaintext desc:Power Filter (Drive, LFO, Env & Visualizer) author:Gemini version:1.2 slider1:1000<20,20000,1,log>Cutoff (Hz) slider2:0.707<0.1,10,0.01>Resonance (Q) slider3:0<0,3,1{Lowpass,Highpass,Bandpass,Notch}>Filter Model slider4:0<0,100,0.5>Drive (%) slider5:0<-100,100,1>LFO Amount (%) slider6:1<0.1,20,0.1>LFO Rate (Hz) slider7:5000<0,15000,1>Env Mod (Hz) slider8:10<1,1000,1>Env Attack (ms) slider9:100<1,2000,1>Env Release (ms) @init ic1eq = ic2eq = ic1eq_r = ic2eq_r = 0; lfo_phase = 0; env_val = 0; gate = 0; @slider drive_val = slider4 * 0.05; lfo_inc = 2 * $pi * slider6 / srate; a_coeff = 1 - exp(-1 / (slider8 * srate / 1000)); r_coeff = 1 - exp(-1 / (slider9 * srate / 1000)); @block while (midirecv(offset, msg1, msg2, msg3)) ( status = msg1 & $F0; status == $x90 && msg3 > 0 ? gate = 1 : (status == $x80 || (status == $x90 && msg3 == 0)) ? gate = 0; midisend(offset, msg1, msg2, msg3); ); @sample target = gate ? 1 : 0; env_val += (target - env_val) * (gate ? a_coeff : r_coeff); lfo_phase += lfo_inc; lfo_phase > 2 * $pi ? lfo_phase -= 2 * $pi; lfo_sig = sin(lfo_phase); // Calculate modulated cutoff cur_cutoff = slider1 + (lfo_sig * slider5 * 20) + (env_val * slider7); cur_cutoff = max(20, min(cur_cutoff, 20000)); // SVF DSP g = tan($pi * cur_cutoff / srate); k = 1 / slider2; a1 = 1 / (1 + g * (g + k)); a2 = g * a1; a3 = g * a2; v0 = spl0; v3 = v0 - ic2eq; v1 = a1 * ic1eq + a2 * v3; v2 = ic2eq + a2 * ic1eq + a3 * v3; v1 = tanh(v1 * (1 + drive_val)); ic1eq = 2 * v1 - ic1eq; ic2eq = 2 * v2 - ic2eq; v0r = spl1; v3r = v0r - ic2eq_r; v1r = a1 * ic1eq_r + a2 * v3r; v2r = ic2eq_r + a2 * ic1eq_r + a3 * v3r; v1r = tanh(v1r * (1 + drive_val)); ic1eq_r = 2 * v1r - ic1eq_r; ic2eq_r = 2 * v2r - ic2eq_r; slider3 == 0 ? (spl0=v2; spl1=v2_r) : slider3 == 1 ? (spl0=v0-k*v1-v2; spl1=v0r-k*v1r-v2r) : slider3 == 2 ? (spl0=v1; spl1=v1r) : (spl0=v0-k*v1; spl1=v0r-k*v1r); @gfx 400 200 // --- Background --- gfx_r=0.1; gfx_g=0.1; gfx_b=0.15; gfx_a=1; gfx_rect(0,0,gfx_w,gfx_h); // --- Draw Frequency Response Curve (Simplified) --- gfx_r=0.5; gfx_g=0.5; gfx_b=0.8; gfx_a=0.4; ctx = cur_cutoff; cx = (log(ctx/20) / log(20000/20)) * gfx_w; // Logarithmic X mapping // Draw "Model" Shape gfx_x = 0; gfx_y = gfx_h; i = 0; loop(gfx_w, freq = 20 * exp(i/gfx_w * log(20000/20)); // Very basic approximation of the filter slope for the GUI slider3 == 0 ? (h = freq > cur_cutoff ? exp(-(freq-cur_cutoff)/cur_cutoff) : 1); // LP slider3 == 1 ? (h = freq < cur_cutoff ? exp(-(cur_cutoff-freq)/freq) : 1); // HP slider3 == 2 ? (h = exp(-abs(freq-cur_cutoff)/(cur_cutoff*0.5))); // BP slider3 == 3 ? (h = 1 - exp(-abs(freq-cur_cutoff)/(cur_cutoff*0.1))); // Notch gfx_lineto(i, gfx_h - (h * gfx_h * 0.8), 1); i += 1; ); // --- Draw Modulation Indicator (The moving line) --- gfx_r=1; gfx_g=0.8; gfx_b=0; gfx_a=0.8; gfx_line(cx, 0, cx, gfx_h); gfx_x = cx + 5; gfx_y = 10; gfx_drawstr("CUTOFF"); ```