/ REF / hzplugins / genish ops

Songs.hz . Music API
Hz.Plugins . 3rd Party Plugins
General MIDI . MIDI CC . MIDI notes
Configure
home . Topics . Interface . Reference . Examples


Implementation | Constants | Special | Triops | Binops | Monops
last update: 8/2/2026, 1:14:24 PM

Intro

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.

Implementation

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.

Constants

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.

Special

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.

Triops

Usage Notes
mul3(x,y,z) x*y*z, faster than mul(x, mul(y, z))
madd(x,y,z) x*y + z

Binops

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

Monops

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.