Wavelet Explorer
The wavelet, its spectrum and the time-frequency map of a real epoch, all driven by the same two numbers — and then the same epoch through multitaper and filter-Hilbert beside it.
4 claims on this page are unverified. TODO(confirm) marks a specific statement the author has not yet checked against a
primary source. Everything else on this page has been reviewed. Treat a marked claim as
provisional and go to the cited source rather than quoting the sentence.
Modes: morlet · compare — the widget below runs in morlet. Use Share state to put the exact view in the URL.
What it does
A Morlet wavelet is drawn, its spectrum is drawn, and the time-frequency map of one real 3-second epoch is computed from it — all three from frequency and cycles, so moving either slider moves all three at once. The two widths the lesson asks for are printed as figures with the convention named, and the edge-effect margin is shaded on every panel, per frequency row.
compare mode puts three client-side estimators on the same epoch — Morlet, multitaper and filter-Hilbert — beside the precomputed multitaper map from the data script, with a panel explaining whichever one is selected: the DPSS tapers and their concentration ratios, or the band-pass response.
Everything is computed in the browser except the reference multitaper map, which is labelled as such.
The FWHM convention
The wavelet is w(t) = C · exp(2πift) · exp(−t²/(2σ_t²)) with σ_t = n_cycles / (2πf), which is mne.time_frequency.morlet’s definition. Widths are quoted by default as the full width at half maximum of the Gaussian amplitude envelope:
FWHM_t = 2 σ_t √(2 ln 2) = n_cycles · √(2 ln 2) / (π f)
σ_f = f / n_cycles
FWHM_f = 2 σ_f √(2 ln 2) = 2 f √(2 ln 2) / n_cycles
FWHM_t · FWHM_f = 4 ln 2 / π = 0.8825424 s·Hz (for every f and every n_cycles)
FWHM_t is bit-identical to mne.time_frequency.fwhm. The power convention — the FWHM of |w|², a factor √2 narrower — is offered as the second option in the “widths quoted as” control, and the widget always says which is on screen. At 10 Hz with 7 cycles the two conventions give 262.347 ms / 3.36403 Hz and 185.507 ms / 2.3787 Hz; a number quoted without its convention is ambiguous by 41 %, which is why L4.2’s exercise names it.
Note that the widget opens on the data script’s own cycle scheme rather than on a fixed 7, so its first reading at 10 Hz is 5 cycles — 187.4 ms and 4.710 Hz, a different wavelet. The panel always says which cycle count it is describing.
Controls
| Control | What it sets |
|---|---|
| Frequency | The wavelet drawn, and the frequency the width figures describe |
| Cycles | Three schemes: the data script’s scheme × a factor (the default — the per-frequency list the sidecar records, max(3, f/2), times a slider); a fixed number (1–20, opening on 7); or a ramp with frequency between two values |
| Widths quoted as | amplitude (default) or power |
| Epoch length | 0.25 s to the stored length, cropped symmetrically about the centre |
| Frequency axis | The data file’s own 37-row list (so this map and the stored multitaper map are comparable cell by cell) or a logarithmic axis between two values |
| Map from / to | 0.5–120 Hz, disabled while the file’s own axis is in use |
| Power scale | log₁₀ or linear — a colour scale, not a change to the data |
| Shade the edge-effect margin | On or off; drawn per frequency row |
Estimator (compare) | Morlet · multitaper · filter-Hilbert · stored |
Time-bandwidth product (compare) | 2–10, multitaper only |
The map takes focus and responds to the arrow keys (shift for a bigger step, Escape to clear the cursor, Enter to set the wavelet frequency to the row under the cursor); its readout is in a live region.
What to look for
morlet
- Move the cycles slider and watch the two widths move in opposite directions while their product stays pinned at 0.8825 s·Hz. That fixed product is the trade-off: cycles do not buy resolution, they spend it on one axis to gain it on the other.
- At 7 cycles and 10 Hz the wavelet is 262 ms wide at half amplitude and 3.36 Hz wide in frequency. Drop to 3 cycles and the same wavelet is 112 ms and 7.85 Hz: the burst onset sharpens and the alpha row smears across the beta range.
- The shaded margin is where the wavelet reaches past the epoch and the convolution is reading padding rather than data. It is 5 σ wide, so it grows with cycles and falls with frequency; at 3 Hz with 7 cycles it is 1.86 s per side, which swallows a 3-second epoch whole (
pf-tf-edge-effects). - Shorten the epoch and the map turns into almost all margin. This is why an analysis epoch is cut longer than the window you intend to report and trimmed afterwards — padding does not create the data the wavelet needs, it only hides that the wavelet is reading zeros.
- Switch the cycle scheme to a ramp. A fixed count gives a temporal width that shrinks as 1/f; a ramp holds time resolution at low frequencies where 7 cycles would be a second long, at the cost of frequency resolution up high. Neither is correct — the choice is a statement about what you are looking for.
compare
- Morlet and multitaper are the same idea with a different window: one Gaussian, several Slepian tapers whose powers are averaged. At a time-bandwidth product of 4 the panel shows the 3 tapers it implies; each alone is a noisy estimate and the average is not.
- Raise the time-bandwidth product and the map smooths across frequency. The band passed is
time_bandwidth · f / n_cycleswide, so at 10 Hz with 7 cycles a product of 4 passes 5.7 Hz and a product of 8 passes 11.4 Hz — a deliberate exchange of frequency detail for a lower-variance estimate, which is why multitaper suits broad high-frequency bands. - Filter-Hilbert agrees closely when its band matches the wavelet’s own spectral FWHM, which is the default. The difference is what each makes easy: filter-Hilbert hands you the instantaneous phase of one band, the other two hand you the whole plane.
- Ask filter-Hilbert for a narrow band on a short epoch and it says it cannot. A zero-phase FIR needs padding of a few times its own length, so the filter is truncated and the transition band widens: the band you asked for is not the band you got — and a filter that narrow makes almost anything look rhythmic (
pf-narrowband-filter-oscillation). - Read the agreement panel in both unit systems. On the shipped epoch the client’s Morlet map and the stored multitaper map correlate at 0.7099 in library units and 0.7001 calibrated to mean-square µV², with a median ratio of 2.398 and 2.131. The same comparison, two rulers — which is L4.3’s exercise.
Used in
- L4.2 STFT and Morlet wavelets (
morlet) - L4.3 Multitaper and filter-Hilbert (
compare)
Data provenance
ds-eegbci — EEG Motor Movement/Imagery Dataset (EEGMMIDB), PhysioNet v1.0.0 (Schalk et al., 2004). ODC-By 1.0, open access, DOI 10.13026/C28G6P. 64 channels at 160 Hz, recorded with no online filter and no notch; 60 Hz mains.
The shipped epoch is subject S001, run R08, channel C3, one right-fist motor-imagery trial (annotation code T2), stored from −1.0 to +2.0 s around the cue — so the cue sits 1.0 s into the epoch — at the dataset’s native 160 Hz. Derived: channel names normalised, average reference over the 64 EEG channels applied offline, channel mean removed over the window; no band-pass, high-pass, notch or resampling. Generated by data/scripts/make_tf_products.py; epoch.json is both the widget index and the §4.5 sidecar of epoch.bin.
The trial was selected. Among this subject’s T2 trials whose largest absolute sample stays under 100 µV, the one with the largest mu-band drop from −1…0 s to 0.5…2 s was taken — a 7.08 dB drop, peak 53.8 µV — with the full ranking in the sidecar. It is a teaching example chosen for legibility, not a sample of anything.
multitaper.bin is the precomputed map of that same epoch: 37 frequency rows (4–40 Hz), 480 time points, layout: "freqs×times", time_bandwidth 4.0, 3 tapers read back from MNE’s own taper weights rather than assumed, the same n_cycles = max(3, f/2) scheme as the client’s Morlet so the window length matches at every frequency, and the exact tfr_array_multitaper call recorded in exact_call.
Units. Neither map is band power in µV². Both are the analysis library’s |W ∗ x|² under its own wavelet normalisation, which carries a frequency-dependent constant. Each sidecar ships unit_cosine_power_uv2 — what that transform returns for a unit-amplitude cosine at each frequency, measured on a test signal — and the widget divides each row by twice it before drawing, which puts both maps on one mean-square µV² scale. Where a sidecar has no such constant the view is labelled “library units — uncalibrated” rather than carrying a false µV².
TODO(confirm): ds-eegbci’s own online reference is not documented in the catalogue, so the offline average reference is this site’s choice and the sidecar records it as such.
Open the code
site/src/components/widgets/w-wavelet-explorer/ — Widget.svelte, morlet.ts, dpss.ts (Slepian tapers by Sturm bisection and inverse iteration), estimators.ts, state.ts, data.ts, synthetic.ts, colormap.ts, plots.ts, fixtures.ts (SciPy and MNE reference values with the exact call beside each block), and 145 tests. Repository link: TODO(confirm) (GitHub org/repo, §13 item 3).
TODO(confirm): the widget reports a possible upstream issue in MNE’s _make_dpss, which can index past a taper list that low_bias has shortened; here the surviving tapers are used and the shortfall is reported. TODO(confirm): screenshot.png is a placeholder, not a capture of the running widget.