SCWF Lab

A single-cycle waveform and wavetable editor that runs entirely in your browser. Nothing is uploaded, nothing is installed, and it works with the network off.

What it is

A single-cycle waveform is one period of a sound, stored as a short chunk of samples — typically 2048 of them. A synth loops it to make a note. A wavetable is a stack of those cycles that a synth sweeps through, so the timbre changes as the note plays.

This tool builds both. You do not draw the waveform directly; you set the level of each partial — the whole-number multiples of the cycle's own rate — and the waveform is worked out from them. Partial 1 is the fundamental, partial 2 is an octave above it, partial 3 an octave and a fifth, and so on.

1 2 3 4 5 6 7 8 partial levels becomes one cycle
Levels falling off as 1/k give a sawtooth. Every shape in this editor is built this way, which is why it can never land between two partials.

Start here

  1. Pick a Note in the header. That sets what the cycle is tuned to — everything else is relative to it.
  2. Press a preset on the right (Saw, Square, Organ…) to get a starting spectrum.
  3. Hit Space, or turn on ⌨ Keys and play ak.
  4. Drag on the spectrum to change it. The waveform and the sound follow immediately.
  5. Press ↓ Cycle to save one waveform, or ↓ Wavetable to save the whole morph.

Everything is auto-saved in this browser as you go, so a refresh will not lose your work. Use Save… to keep a copy you can come back to or send to someone.

The spectrum

The big panel is the heart of it: partials across, level up, measured in decibels. The bottom axis is labelled with the frequency each partial will sound at.

GestureDoes
DragPaint levels — set the partials under the cursor
Shift-dragNudge a band up or down in dB, keeping its shape
Alt-dragErase back to silence
Wheel, or [ ]Brush width — how many partials you paint at once
Drag the strip belowSelect a band of partials to work on

The brush tapers at its edges rather than painting a rectangular block, so a wide brush leaves a smooth hump instead of a step.

Under the spectrum is the phase lane, and under that the cycle the two of them produce. That pane's horizontal axis is one period — ticked in quarters, with the period in milliseconds in its readout — not samples or seconds, because the same cycle can be played at any pitch. Phase has its own section because it is the least obvious part of the whole tool.

Scope and ops

The buttons on the right — level changes, tilt, smooth, randomise, the presets — do not always apply to everything. They apply to the current scope, which is two things combined:

They stack. Odd plus a band from 20 to 60 means "the odd partials between 20 and 60", so "take 3 dB off the upper odd harmonics" is two clicks and a drag.

OpDoes
−6 −1 +1 +6Change level in dB
MuteSilence the scope
SoloSilence everything outside the scope
FillWrite the rolloff shape from the box below
NormScale so the loudest partial in scope is full
SmoothAverage each partial with its neighbours
RandScatter the levels
Spectral tiltTip the spectrum by so many dB per octave — the quickest brightness control

Presets replace the whole spectrum when nothing is scoped. With a band selected or a parity chosen they only write inside the scope — which is how you get an organ in the low partials and a saw above it. The shape is levelled to fill the band, because a preset's own levels fall away with partial number and writing them raw into a high band would land some 30 dB down and look like nothing happened. One preset is an honest exception: Vocal is a set of formants that live in the low partials, so high up it has nothing to write and says so rather than clearing your band.

Phase

Phase decides where each partial starts, not which partials sound. On a steady note in mono that is very close to inaudible — the ear is largely deaf to the phase of a sustained tone. So it is fair to ask what it is for.

It changes the peak, and therefore the loudness

The same partials at the same levels can stack into a waveform with a tall spike or a flat plateau, depending on how they line up. The crest figure on the cycle pane is the difference between the peak and the average. It matters because everything here is peak-normalised on the way out: a flatter waveform comes back louder after normalising, and leaves more room before anything downstream clips.

No preset is best

It is tempting to assume one phase arrangement is always flattest. It is not. Crest factor measured on this tool's own presets, in dB — lower is flatter:

SpectrumZero phaseRamp (spread)
Saw (1/k)6.189.08
Square1.445.06
Organ (dense)17.5910.27

Measured on this tool's presets at its default 256 partials — the figures move with the partial count, so treat them as the shape of the result rather than constants.

Zero phase is the flattest thing you can do to a sawtooth and among the worst for an organ. That is what Lowest peak is for: rather than trusting a formula it tries the presets and a spread of random scatters, keeps whichever measures flattest, and tells you what it moved.

The other two uses

Staying in tune

You cannot detune a waveform by editing it. Everything is built from whole multiples of the cycle, so the result is exactly periodic by construction. There is nowhere to put energy that is not a whole partial.

What can change is which fundamental your ear picks out, because that is the greatest common divisor of the partials actually sounding — not simply partial 1.

all partials — sounds at partial 1 gcd 1 — in tune even partials only — an octave up gcd 2 — one octave high The editor marks both: where the fundamental should be, and where it actually is.
Mute the odd partials and you have genuinely built a waveform an octave up. Leave 3, 6, 9 and it is a twelfth up.

So the spectrum shades the column where the fundamental has to sit for your chosen note and marks it with a caret at the top — green when the sounding pitch agrees, amber when it does not — and puts a red caret at the bottom on the partial actually being heard. They sit at opposite edges because the two can be a few pixels apart. The readout says it in words:

fundamental p1 65.41 Hz C2 ✓they agree
want p1 65.41 Hz C2 · sounds p2 130.81 Hz C3 ⚠an octave high

A missing fundamental is correctly not flagged. Partials 2, 3, 4, 5 with no partial 1 still have a gcd of 1, and your ear still hears the fundamental — which is how a small speaker fakes a bass note.

The one place real detuning can get in is a cycle chopped from a recording at slightly the wrong length. That view has its own check for it.

Sub-octaves

A cycle can only hold whole multiples of its own rate, so "an octave below the note" needs the note to sit higher up the series. The Sub control puts the note on partial 2 or 4 and retunes playback to match. The partials underneath it are then real sub-harmonics, marked in green. Watch the tuning readout while you use it: with the note on partial 2, adding partial 3 drops the perceived fundamental an octave.

One cycle or a table

The Project switch in the header decides which you are making.

Switching between them never throws anything away: the other keyframes are set aside and come back when you switch to Wavetable.

Morphing

Keyframes are the wavetable. Each holds a full spectrum, sits at a position from 0 to 1, and carries the easing used on the way out of it. The table is sampled at the frame count you choose and every frame in between is worked out for you.

ControlDoes
+ Add / Dup / DelAdd a keyframe in the gap, copy the current one, remove it
The slider on a keyframeWhere it sits in the table
Its easinglinear, smooth, fastIn, fastOut, or hold for a hard step with no morph at all
FramesHow many cycles the table holds — 256 is the usual convention
Morphspectral glides partials in and out; waveform cross-fades the cycles
LevelSee below — it decides what "as loud as it should be" means

Level

Stereo

Switch Channels to Stereo and each channel gets its own spectrum in every keyframe — a different waveform left and right.

Link L+R is on by default. While it is on, drawing, presets and every op write to both channels at once. That is usually what you want, and it is also the reason a preset can seem to "overwrite the other channel". The line under the L/R tabs always tells you what the next edit will touch. Turn Link off to give each side its own sound.

L → R and R → L copy one side onto the other, which is a good starting point before nudging one of them out of step. With a band or a parity selected they copy only that part, leaving the rest of the destination alone.

Width, and why 180° is a trap

Width scatters the right channel's phases away from the left's. Neither spectrum changes — both sides stay the same sound — only their alignment differs, and that is what stereo width physically is.

Beside the slider is the figure that matters: what survives if the two channels are summed to mono. Someone will play your sound on a phone speaker, and this tells you what they will hear.

SettingIn mono
Width 0% (Match L)0.0 dB — nothing lost
Width 25%−0.4 dB
Width 50%−1.6 dB
Width 100%−6.6 dB
Invert R (180° on every partial)silence

So: does it ever make sense to have the two channels 180° apart? It is the widest thing you can do, and it is also the one setting that disappears completely when the two are summed — every partial cancels against its twin. Invert R is in the panel so you can hear that for yourself, not because it is a good place to leave things. Anything short of a full inversion degrades gracefully; a full one does not.

Importing a cycle

↑ Import WAV… reads a single-cycle WAV back into a spectrum, so you can edit a cycle you already have or build a table out of several. It reads PCM at 8, 16, 24 and 32 bits and floating point at 32 and 64.

You importIn single cycleIn wavetable
One fileReplaces the waveformAdded as a keyframe, in the widest gap
Several filesThe first onlyThey become the keyframes, spread evenly
A multi-cycle fileIts first cycleUp to 16 cycles, spread evenly

Selecting several files at once is the quick way to turn a folder of single-cycle waveforms into a morphing wavetable. Two things do not survive the trip: partials above the editor's partial count (it says how many it kept), and any DC offset.

Chopping a recording

✂ Chop… loads something longer — anything your browser can decode, so mp3, ogg, flac and m4a as well as WAV — and lets you cut a single cycle out of it. ● Rec captures from a microphone or input into the same view.

You get the whole file across the top, a zoomable detail below it, and a shaded marker over the cycle that will be taken.

ControlDoes
Drag the top stripMove the view along the file
Drag the detailPlace the start of the cycle
Wheel over the detailZoom
DetectFind the period under the cursor and set the cycle length to it
SnapMove the start to the nearest rising zero crossing
÷2 / ×2Halve or double the cycle — an octave either way
▶ Span / ↻ CycleHear the visible region, or loop just the marked cycle
Take1 takes the cycle at the cursor; more takes that many spread across the visible span

Taking several across one evolving note is how you turn the attack and the sustain of a real instrument into the two ends of a morph.

Watch the pitch readout. It compares the cycle length you are about to take against the pitch it detects in the audio there, and turns red when they disagree — which is what catches a marker sitting across one and a half periods. That is the one way to get a genuinely out-of-tune waveform out of this tool.

Detection is deliberately not simple autocorrelation, which locks onto multiples of the period: on a clarinet-like tone it reports 46.6 Hz for a note actually at 233.1 Hz. The method used here lands within a cent on that, and also finds the period of a tone whose fundamental is missing altogether.

Recording needs microphone permission, which the browser will ask for. The sample itself is never part of a saved project — what you keep is the cycle you took out of it.

Playing it

▶ Play in the transport plays one note using the settings beside it. ⌨ Keys turns your computer keyboard into a one-octave instrument instead.

ControlDoes
atWhich note to play — it does not have to be the note you are editing
MorphHold at the slider's position, or sweep across the table during the note
LenNote length in seconds
A D S RAttack, decay, release in milliseconds; sustain as a percentage
LoopRepeat the note

The envelope fields take a vertical drag or a scroll as well as typing, which is easier than aiming at a spinner arrow.

Notes from the keyboard sustain for as long as you hold the key and several can sound at once. They take their spectrum from wherever the morph slider is sitting rather than sweeping, and each note is normalised to an even loudness. Holding a key while you edit the spectrum re-voices the note as you draw.

Exporting

ButtonWrites
↓ CycleOne single-cycle waveform — the selected keyframe
↓ Set…N single cycles as N separate files, in one zip
↓ WavetableEvery frame end to end
↓ AudioThe preview render, envelope and morph included

The defaults — 2048 samples per cycle, 256 frames — are the convention Serum and similar synths expect, and the cycle, set and wavetable exports carry a small text chunk naming the frame size so those synths slice them correctly. Other software ignores that chunk harmlessly.

In stereo you get an interleaved stereo file. Most wavetable synths want mono, so use Mono unless you specifically want a stereo waveform.

Choosing a format

Two menus sit to the left of the export buttons, and they answer two separate questions.

The first is what wraps the audio. WAV writes a normal RIFF file that says in its own header how to read it. Raw writes the samples and nothing else — no header at all — which is what some hardware and the stricter wavetable importers insist on. A raw file carries no record of its sample rate, bit depth, channel count or byte order, so the status line spells all four out after the export; write them down, because nothing in the file will tell you later. Raw files get a .raw extension, and a little-endian / big-endian menu appears next to it, since a headerless file leaves byte order open too.

The second is how each sample is encoded: 8-, 16-, 24- and 32-bit integers, or 32-bit float. Integers are signed two's complement everywhere, with exactly one exception, which is not ours to change: 8-bit inside a WAV is unsigned, because the WAV format requires it. The same 8-bit samples written raw are signed. If something has asked you for "headerless signed 16-bit", that is Raw + 16-bit + little-endian.

32-bit float is the only format that can carry a sample above full scale, which matters if you export as authored — an additive sawtooth peaks near 1.85, and every integer format clips it flat.

A set of individual files

↓ Set… is for an importer that wants a folder of single-cycle files rather than one long wavetable. A browser cannot hand over a folder, so they arrive as a zip.

Morph frames takes N cycles spread evenly across the morph, exactly as the wavetable export would at that frame count — set N to the frame count and the two agree frame for frame. Keyframes takes your keyframes as they are, one file each, with no interpolation between them. Files are numbered from 1 and zero-padded to the width of the largest number, so an alphabetical sort — which is what an uploader will do — is also the morph order. The Name field sets the stem; anything a filesystem or an archiver would choke on is stripped out of it.

Levelling follows the same Level menu as everything else, so a set and a wavetable of the same frames are equally loud. The preview line in the dialog shows the exact first and last file name and the total size before you commit to it.

Saving your work

Save… writes a .json project holding every keyframe, both channels, and all your settings. Load… reads one back. The current session is also kept in this browser automatically, so closing the tab is safe — but that copy lives only in this browser on this machine, so use Save… for anything you want to keep.

Undo

↶ Undo and ↷ Redo sit above the keyframe list, on Ctrl+Z and Ctrl+Shift+Z (Cmd on a Mac). Forty steps are kept. A whole drag counts as one step rather than one per pixel, and selecting a band is not an edit at all, so it does not fill the stack. Copy and Paste in the Clipboard panel are still useful for parking a spectrum you want to come back to.

Keys and gestures

KeyDoes
SpacePlay or stop the preview
19Select that keyframe
[ ]Brush narrower / wider
Ctrl+ZUndo
Ctrl+Shift+ZRedo

With ⌨ Keys on, one octave of the computer keyboard plays instead:

KeyNote
a s d f g h j kThe white keys, C up to the C above
w e   t y uThe black keys between them
z xOctave down / up

The drawn keyboard lights up as you play and can be clicked with the mouse.

Why does it…

…apply a preset to both channels?

Link L+R is on. See Stereo.

…say my waveform sounds an octave up?

Because it does. See Staying in tune — the pitch you hear is the greatest common divisor of the partials sounding, and muting the odd ones really does build a waveform an octave higher.

…sound the same when I change phase?

On a steady note in mono it very nearly does. Phase changes the waveform's peak rather than its tone. See Phase.

…show a peak above 1.0?

Adding partials together overshoots — an additive sawtooth peaks near 1.85. Exports are normalised for you unless you choose "as authored".

…ignore my edits?

Check the scope. A band selection or an Odd/Even choice left over from earlier will restrict every op to those partials. Clear next to the selection readout resets it.

…refuse my microphone?

The browser has to ask permission, and some browsers will not grant it to a page opened directly from a file. The editor will say so rather than failing silently.

Known limits