lightbulb Note A genish script is a JavaScript function that returns a single object representing a sound generating or filtering program.
When a script returns an array of two values it is
interpretted as a stereophonic synth or effect.
When a single value is returned it is either a
voicemgr stereo synth or a monophonic synth/effect.
Here is a trivial script that represents stereophonic silence.
function silence2(g)
{
return [g.add(0, 0), g.add(0, 0)];
},
When active, it is invoked tens of thousands of times per second (ie the sample rate).
A synth or effect is described by a chain of
genish operators. These are available through
the genish context, g.add above.
Operators prescribe parameters of two classes, variable
or initial.
Variable parameters can receive values either as numerical values or as a result of another operator. We describe the interconnections of operators in your script as a chain or graph.
Initial parameters are used to initialize an operator-instance.
Below, we indicate initial parameter with *. Make sure
not to place an operator in these parameter positions.
Our implementation is a rewrite of Charlie Roberts'
genish.js repo
circa early 2026. It was inspired by one or more of the various
genish.js wasm branches. Our rewrite uses emscripten and c++
to produce GenishCore.js.
Our implementation is ongoing in that the set of operators is in flux. Let us know which are missing and why they are important to you.
Generating Sound & Organizing Time
is an excellent book by the authors of gen~ (Wakefield, Taylor). It
presents a deep dive into the possibilites of this approach. Please
note that gen~ is more complete and more efficient than Hz.Genish
due to a native JIT engine that runs directly on your desktop cpu.
Still and all, you can do some pretty cool things with Hz.Genish in
your browser.
Hz.Genish is built atop a WASM runtime that implements
a genish-graph interpretter with runtime specialization.
Unlike Max gen~ there is no intermediate compilation step
between your code and the interpretter.voicemgr and adsr operator
to simplify your task of implementing a viable synth.param to support auto-gen of a simple GUI comprising
sliders and knobs.fm2.The genish context includes a few useful constants.
| Name | Notes |
|---|---|
g.SRATE |
sample rate, typically 44100 or 48000. Convert seconds to samples by multiplying by SRATE. |
g.SRATE_INV |
1.0 / g.SRATE, Convert samples to seconds by multiplying by SRATE_INV. |
g.PI |
Used with trig functions. |
g.TWOPI |
|
g.PIOVERTWO |
|
g.FREQ_TO_NYQ |
Converts a frequency in Hz to percent of Nyquist 0-1. Useful for eg svf. |
| Usage | Description |
|---|---|
data(dataref, type) |
Returns an object suitable for passing to peek or poke.When dataref is a string, it's interpretted as a audio filepath. This will be asynchronously loaded, but performance won't begin 'til the data-load is complete. If dataref is a javascript array peek and poke can proceed with their operations immediately. |
peek(data, phase, interp*, mode*) |
reads from data locationa as returned by data() op. interp:floor,linear,cubic mode:phase,samples |
poke(data*, value, index) |
writes into data location, as returned by data() op |
accum(incr, result, min*, max*, init*) |
accumulates a value by incr. Result is wrapped into min, max. |
phasor(frequency, reset) |
produces a linear increasing ramp (phasor) between 0 and 1. Frequency is expressed in Hz. |
cycle(frequency, initialphase=0*) |
produces a sinusoid (cycle) between -1 and 1. Frequency is expressed in Hz. |
sqrwave(frequency, initialphase=0*) |
produces an antialized square wave between -1 and 1. Frequency is expressed in Hz. |
sawwave(frequency, initialphase=0*) |
produces an antialized saw wave between -1 and 1. Frequency is expressed in Hz. |
triwave(frequency, initialphase=0*) |
produces an antialized triangle wave between -1 and 1. Frequency is expressed in Hz. |
sah(input, control, threshold) |
sample and holds input signal when control signal crosses threshold. |
memo(input) |
produces a value, guaranteed to be constant per-frame. Without memo, each reference to input will result in a function call. |
caller(input, dataOffset) |
looks up the parameter of input. Requires an (obscure) index representing the value of input's parameter or state to return. |
counter(incr, reset, max, initVal=0*) |
similar to phasor, but you choose incr and max. |
bus(gain, size=10*) |
returns an object with connect(in1, in2...). Adds up to size inputs and scales their sum. |
bang() |
returns an object with trigger() and triggerWhen(when). Produces impulse. |
ad(attackSec*, decaySec*, maxlevel=1) |
triggerable two-state envelope. Returns an object with trigger() and triggerWhen(when) |
adsr(attackSec*, decaySec*, sustain=.8*, releaseSec*, maxlevel=1) |
triggerable 4-state envelope. Returns an object with trigger() and triggerWhen(when), release() and releaseWhen(when). |
ssd(input), () |
single sample delay. With no args, returns object with fields: .in(input) and .out to facilitate wiring into feedback loops. |
delay(input, delaysamps, maxdelaysamps*) |
interpolating delay line. Delay times can be converted to samps my multiplying by g.SRATE. |
slide(input, slideUpSec, slideDownSec) |
smooths the input signal over intervals expressed in seconds. (portamento). |
param(initval, name?, cfg?) |
Returns an object with a value field that can be read or written to. Used to modify the behavior of running ops via program or GUI. name is an optional parameter that if provided registers the parameter to the GUI. Additional GUI hints can be provided via cfg:{min, max, group, default, label}. |
mix(in1, in2, t) |
returns a mix of in1 and in2 according to t which should be between 0 (in1) and 1 (in2). |
ifelse(a, b, c) |
evaluates b or c according to a == 0 ? b else c. |
clamp(a, b, c) |
clamps the value of a to a value between b and c. |
noise(seed*) |
produces a white-noise signal between -1 and 1. |
rand(seed*, min*, max*) |
produces a random number signal between min and max. |
_in1 |
returns the value of the Hz.Genish node's left input. If no input is connected, returns 0. |
_in2 |
returns the value of the Hz.Genish node's right input. If no input is connected, returns 0. |
svf(input, cutoff, resonance, type*) |
returns a filtered version of input. Filter cutoff is between 0 and 1. Use g.FREQ_TO_NYQ to convert a frequency to a cutoff. Resonance should typically range between .5 - 1. type is one of 'lo'/0, 'hi'/1, 'band'/2, 'notch'/3. |
| Usage | Notes |
|---|---|
mul3(x,y,z) |
x*y*z, faster than mul(x, mul(y, z)) |
madd(x,y,z) |
x*y + z |
| Usage | Notes |
|---|---|
add(x,y) |
x + y |
sub(x,y) |
x - y |
mul(x,y) |
x * y |
div(x,y) |
x / y |
and(x,y) |
float(x != 0 && y != 0) |
or(x,y) |
float(x != 0 || y != 0) |
gt(x,y) |
float(x > y) |
gte(x,y) |
float(x >= y) |
lt(x,y) |
float(x < y) |
lte(x,y) |
float(x <= y) |
eq(x,y) |
float(x == y) |
neq(x,y) |
float(x != y) |
gtp(x,y) |
x > y ? x : 0.f |
ltp(x,y) |
x < y ? x : 0.f |
min(x,y) |
|
max(x,y) |
|
pow(x,y) |
x ** y, x ^ y |
mod(x,y) |
mod(2.1,1)==.1, mod(-2.1,1)==-.1, mod(-2.1,-1)==-.1 |
| Usage | Notes |
|---|---|
floor(x) |
truncates a number to the next lower integral value. |
ceil(x) |
truncates a number to the next higher integral value. |
round(x) |
truncates a number to the nearest integral value. |
abs(x) |
ensures x is always positive. |
sqrt(x) |
|
sin(x) |
multiply by g.TWOPI to convert frequency to radians |
cos(x) |
multiply by g.TWOPI to convert frequency to radians |
tan(x) |
use tan with care, it can produce infinite values. |
asin(x) |
returns inverse-sin values between -PI/2 and PI/2. |
acos(x) |
returns inverse-cos values between -PI/2 and PI/2. |
atan(x) |
returns inverse-tan values between -PI/2 and PI/2. |