Wavetable Lab

@stvemillertime2048 × 64 wavetables

Keyframe frame 1

The strip under the editor is the keyframe's spectrum: harmonics 1–1023 on a log scale, 72 dB tall. The dashed line marks the spectral centroid, a rough measure of brightness. Ghosts draws the previous keyframe (dashed) and the next one (solid) behind this one. Wet overlays the frame after transforms, and its spectrum as ticks. The header shows peak, RMS, DC offset, centroid (in harmonics) and zero crossings. If the loop point jumps, a red seam tag appears; click it to smooth that keyframe's loop point.

25%
1023
100%

Shapes are generated band-limited: the waveform is built at 16× resolution and then cut to the chosen number of harmonics. Blend below 100% mixes the shape into the existing keyframe.

Drag the bars to set harmonics 1–64. Editing replaces the keyframe with a sum of sine partials, so phase information and harmonics above 64 are dropped.

0.35 oct
±2 fr
3.0 dB

Paint works on the Spectrum view of the table (harmonics up, frames across): Boost raises harmonics, and can draw in new ones from silence; Cut lowers them; Erase removes them. Height is the brush size in octaves, width in frames on each side. Phase tools keep each harmonic's level and change only its phase. Match selected makes keyframes phase-coherent so crossfades don't dip. Phase tools use the all keyframes box above. Blur over time smooths harmonic levels across neighbouring keyframes.

Vars x (0–1) p=2πx t table pos (0–1) i frame · ⌘Enter applies

Functions: everything in Math (sin cos tanh abs pow sqrt exp log floor round min max…) plus pi tau fract mod clamp mix saw(x) sq(x) tri(x) pulse(x,w) rand(seed) sum(n, k => …). The output is band-limited and normalized per frame, so any range works. Whole table writes one keyframe per frame.

Draw directly on the keyframe editor above. Use Smooth afterwards to tame harsh edges, and Align to zero to avoid a click at the loop point.

Edit the keyframe as nodes joined by curves, right on the editor above. Click empty space to add a node and drag to move it; it stays between its neighbours. Drag a curve up or down to bend it, and double-click a curve to straighten it. Double-click or right-click a node to delete it. The first and last nodes stay at the ends of the cycle and move up and down together, so the loop never jumps. Convert to nodes fits up to the chosen number of nodes to any keyframe. Set the morph to Shape (nodes) and the frames between two node keyframes move each node and bend from one shape to the other instead of blending harmonics; when the two have different numbers of nodes, the curves of the one with fewer are split until they match, which doesn't change its shape. Pairs where either keyframe has no nodes use Spectral. Normalize, Invert, Reverse and Remove DC keep the nodes; the other tools turn them back into plain samples (Restore nodes undoes that).

Drop WAVs, a folder, a Vital or Surge table or an image here (or anywhere on the page)
or

Auto-detect reads the frame size from a _wt1024 suffix in the filename or from a Serum clm chunk, then falls back to 2048. A short file is treated as one cycle. Tables with more than 64 frames are thinned to 64 evenly spaced frames, the same as Tonverk does. Several WAVs at once (or a folder) become one table with one keyframe per file. Vital .vitaltable and .vital files load their wave keyframes; extra tables in a preset go to the bank. Surge / Bitwig .wt files load like a wavetable WAV. Serum wavetables are plain WAVs and load like any other.

Drop a long sample here (WAV, AIFF, MP3, FLAC…), or or
0.00 s
0.00 s
none

Drag across the waveform to pick a region, or drag its edges. At each frame position the pitch is found (YIN), one or more cycles are cut at that exact period, resampled to 2048 samples and made loop-clean. At note onsets puts one frame in each note (up to the frame count), and each note finds its own pitch. Drum loop sets up a raw chop with one frame per hit, for drums and other unpitched audio. Vocode doesn't look for a pitch at all: it measures the sample's spectral envelope at each frame and lays it over a full harmonic series at the Hz you set, so drums, noise and speech become pitched tables that keep their tone colour (Cycles avg averages that many neighbouring windows). Sample B fills the second half of the table from a second file (its non-silent part), with a spectral morph across the blend frames. Changing a setting runs it again. 🎙 Record takes up to that many seconds from the microphone (click again to stop early) and uses it as sample A, with the silence before you start trimmed off; browsers only offer the microphone on https:// pages and local files, and ask first. Samples aren't saved in the project.

Drop an image here (PNG, JPEG, GIF, WebP…), or

Spectrum reads each frame's line of pixels as a harmonic spectrum: low harmonics on the left (or at the bottom when reading columns, like a spectrogram), and brighter pixels are louder. Waveform reads the line as the wave itself: black is −1, white is +1. Curve bends brightness before it's used (higher keeps only the bright parts). Roll-off tames the top end so a bright image doesn't buzz. The highlighted line on the image follows the scan position. Click the image to scan to that line. Changing a setting runs it again. Images aren't saved in the project.

Keyframes

The frames between keyframes are generated by the morph. Spectral blends harmonic levels, so it avoids the volume dip a plain crossfade has when waves are out of phase. The Table… menu works on every keyframe at once: Reverse frame order flips the table end to end, transform Start → End included; Remove DC centres every keyframe; Fade frame edges fades the first and last 1/64 of each cycle to zero, which removes clicks at the loop point.

Wavetable frame 1 / 64

Oct 3 0.25 Hz 100%

Keyboard: AWSEDFTGYHUJK play notes (chords too), Z/X change octave, C cycles Current / Dry / Snapshot. Press ? for all shortcuts. The frame being played is drawn in orange.

The lane under the table view is its health, after transforms: grey bars are each frame's loudness (RMS) and the blue line its brightness (spectral centroid, log scale). A red mark between two frames is a jump: the loudness changes by more than 3 dB or the brightness by more than half an octave, which you'd hear as a step when the table is scanned. Click the lane to scan to a frame.

Frames: click or drag in the table view to scan; Shift-click or Shift-drag (there or on the keyframe strip) picks a range of frames. Copy takes the picked frames as they are before transforms; Paste replaces the picked frames with them and Insert puts them in front, so the table grows or shrinks (2–64 frames). The copied frames stay while you load another table, so you can paste between bank tables. Duplicate repeats the picked frames, Cut and Delete take them out. The frames an edit doesn't touch keep their sound: keyframes outside it stay, and the frames between it and the nearest keyframe on each side become keyframes. Join tables builds a new table from frames of two tables, transforms included, e.g. 32 from one and 32 from another. Cross-mod ring-modulates, FMs or vocodes the current table with a bank table or the Snapshot slot, frame by frame. Each edit is one Undo step.

Each transform moves from Start (frame 1) to End (last frame). Chain runs top to bottom.

Each transform runs on every frame after the morph and moves from its Start value at frame 1 to its End value at the last frame. The chain runs top to bottom; use ↑ ↓ to reorder it. ∿ gives a transform its own lane: a line you draw across the table (left = frame 1, right = last frame) from Start at the bottom to End at the top, which replaces the shared Start → End curve for that transform. Click ∿ again to go back to the plain ramp. Mix blends a transform back toward what comes into it (100% = fully wet), and 🎲 rolls new settings for that one transform. Waveshaping transforms run at 4× resolution and are band-limited afterwards, so they don't alias. Frames that clip are pulled back to full scale. Even out loudness fixes the volume wobble resynth and fold tables get when you scan them. The table view, audition and export are wet; keyframe thumbnails stay dry.

Notes play a polyphonic wavetable voice, so you hear the table the way a scanning synth plays it: the position glides between frames instead of jumping. The Scan slider, Sweep and the MIDI scan CC set the base position; the mod envelope, LFO and mod wheel move it from there, and the envelope can open the filter too. The sine LFO swings round the scan point; the saws ramp up (or down) from it and wrap round the table, the last frame blending into the first, which is how a Shepard riser rises forever. Hold latches notes until you play them again or press Panic (Esc). Chorus and reverb are on the preview output only. Each octave uses a band-limited copy of the table, so high notes don't alias. Synth settings are saved with the project but don't affect the exported WAV. The strip at the top shows the synth's output while a note sounds (before chorus and reverb): the waveform on the left, steadied on its rising zero crossings, and its spectrum on the right (20 Hz–20 kHz, 90 dB tall).

Starter tables built from shapes, harmonic recipes, formulas, transform Start → End ramps and fixed generator seeds, so they're the same every time. Hovering a card (or focusing it with Tab) shows it in the table view through the Snapshot slot, and can play a note at the current octave; moving away puts the slot back. Load (or double-click, or Enter) replaces the current table and can be undone. A/B keeps it in the Snapshot slot so you can compare while you edit.

★ Mine is your own library, kept in this browser (IndexedDB, or local storage where that's blocked): Save current table stores the table with its transforms under a name, tags and an optional ★. Filter by words in the name or #tag; ✎ renames or retags an entry, ★ marks a favourite and ✕ deletes it. Saving under a name you already used asks before replacing it. It isn't part of the project file, and clearing the browser's site data clears it.

35%

Everything here is seeded: the same seed (and amount) always gives the same result, and the seed goes into the name so you can get back to it. Random table picks 2–5 keyframes from shapes, harmonic recipes and formulas, a morph mode and up to three transforms. Mutate reshapes each keyframe's harmonics with a random EQ curve, nudges the transform settings and sometimes adds one. Add to bank makes variations from seed, seed+1, seed+2…; the Bank then exports them as one ZIP. Shepard riser isn't seeded: it builds a table whose last frame flows back into the first, a whole octave higher each time round, so scanning it with the synth's LFO set to Saw up (or Saw down) gives a rise (or fall) that never ends.

Generate builds a 64-frame table from a small seeded system, one keyframe per frame: Plucked string snapshots a Karplus-Strong string as it decays, bright at the start and darker as it rings out (the seed picks the string length, pluck position and brightness). Logistic map sweeps x → r·x·(1−x) from order into chaos across the table and reads its values as harmonic levels. Lorenz attractor reads harmonic levels along the attractor's path, so the spectrum flows from frame to frame. Cellular automaton runs a one-dimensional rule (30, 90, 110…) and reads each row as a spectrum, the way the Image tab reads an image. These can also go to the bank with Add to bank.

Click a step to jump there; ⌘Z / ⌘⇧Z walk the highlighted line. Editing after a jump starts a new branch and the old one stays here, faded. ☆ pins a step as a named snapshot; Compare loads it into the A/B Snapshot slot. The last 80 steps are kept, and pinned snapshots survive even after their step is dropped.

Template fills in the settings for an instrument and sets the README and report wording; change anything afterwards and it shows as edited. Safe file name keeps file names to letters, digits, _ and -, strips accents and cuts long names so they show up cleanly on hardware screens and copy to a FAT card without surprises. Tuned to sets the frame size to one cycle of a note at the chosen rate (C4 is middle C), for instruments that play frames at a fixed length instead of retuning them; the rate then matters too. Otherwise the sample rate is only written to the header and doesn't change the sound of a wavetable. .wt writes a Surge XT / Bitwig wavetable and .vitaltable a Vital one, with every frame of the table (no template frame limit); both open again here. Spectrogram PNG saves a picture of the table's spectrum (harmonics 1–1023 up on a log scale, frames across, 72 dB of range) with labelled axes, of whatever the table view is showing.

Each table in the bank is rendered with the template, frame size, format and options from Export, and named from its row. Load brings a table back into the editor (undoable); Overwrite replaces the row with what's in the editor now. The bank is saved with the project.

WAV folder + extras puts the WAVs alone in one folder named after the bank, ready to drag onto a card or into a synth's wavetable folder, and the project, report and a README in extras/ so they don't clutter the browser. Loudness evens out level across the bank by RMS, so switching tables doesn't jump in volume: match all tables brings every table to the level of the one with the least headroom, so nothing clips; match to the typical table uses the median level and lets tables that would clip stay at full scale (the report marks them). Either one replaces Normalize table for the ZIP.

Autosave is off

Share link

Cross-modulate

Each frame of the current table, as you hear it, is modulated by the matching frame of another table. Transforms are baked in and turned off; Undo brings the table back.

100%
100%

Join tables

One table from a range of frames of the first, followed by a range of the second, with their transforms baked in. It replaces the current table (Undo brings it back).

Keyboard shortcuts

A–K
Play notes (white keys A S D F G H J K, black keys W E T Y U)
Z X
Octave down / up
[ ]
Scan one frame back / forward (with Shift: 8 frames)
, .
Select the previous / next keyframe
Space
Sweep on / off
Esc
Panic: stop every note, held ones too
C
Cycle Current / Dry / Snapshot
V
Switch the table view between 3D and Spectrum
P
Spectral paint on / off
1–9
Editor tabs: Shape, Harmonics, Spectral, Formula, Draw, Nodes, Import, Resynth, Image
⇧1–7
Bottom panels: Transforms, Synth, Library, Generate, History, Export, Bank
B
Add the current table to the bank
⇧-click / drag
Pick a range of frames in the table view or the keyframe strip
⌘C X V D
Copy, cut, paste over, duplicate the picked frames (⌘⇧V inserts)
Delete
Delete the selected keyframe
⌘Z / ⌘⇧Z
Undo / redo (Ctrl on Windows and Linux; Ctrl+Y also redoes)
⌘S / ⌘O
Save / open project
⌘Enter
Apply the formula (in the formula box)