Visualizers
Two small desktop tools that build intuition for ideas I use professionally in neural signal processing — probability distributions and the Fourier transform — by making you construct them by hand instead of calling a library function and trusting the output.
Dice probability roller
Most dice tools estimate a distribution by rolling thousands of times. This
one doesn’t simulate anything — it computes the exact probability mass
function of a sum of dice by discrete convolution. A single die is a uniform
distribution over its sides; the distribution of a sum of independent dice
is the convolution of their individual distributions, implemented from
scratch as a plain nested-loop convolution and combined across dice with
functools.reduce. Rolling different dice types together (say, 2d10 +
6d6) works the same way one level up: multi_roll convolves each die
type’s own distribution into a running combined distribution.
def roll(num_sides: int, num_dice: int = 1) -> tuple[list[int], list[float]]:
outcomes = list(range(num_dice, num_dice * num_sides + 1))
set_frequencies = [[1] * num_sides for _ in range(num_dice)]
comb_frequencies = reduce(convolve, set_frequencies)
...
No sampling noise, no confidence interval on the estimate itself — the
distribution it draws is the answer. A Tkinter GUI lets you add or remove
dice entries (sides and count) on the fly; the result is drawn directly on
a Canvas — no charting library — as a bar chart with the mean marked by a
vertical line and a 90% confidence interval overlaid as a dashed
double-headed arrow, all positioned and drawn by hand from the computed
outcome/probability pairs.
Try it
This is a from-scratch JavaScript port of the same roll/multi_roll/
convolve functions and the same canvas drawing — not the Tkinter app
itself (that can’t run in a browser), but the identical math, rebuilt for
the web.
Wave & Fourier transform visualizer
A companion tool for the other half of that intuition: what a signal looks like in the time domain versus the frequency domain. Sliders control the amplitude, frequency, and phase of a sine wave; Edit Waveform replaces the current wave, Add Waveform sums a new component into it, so you can build up a composite signal from individual sinusoids and watch its shape change in real time.
The canvas draws two traces side by side: the waveform itself, and its
Fourier transform via numpy.fft, shifted with fftshift so zero frequency
sits at the center. The naive transform clips out to NaN at the edges of
the visible window, so it’s clipped and re-interpolated (np.interp) back
onto the full display range before drawing — a small practical fix for a
naive FFT rather than a real spectral-estimation method, but enough to make
time-domain ↔ frequency-domain intuition visible and interactive, which is
the same intuition behind band-power and spectral-decomposition work on
real EEG/iEEG recordings.
Try it
A JS port of the same make_wave / forward logic — set a sinusoid’s
amplitude, frequency, and phase, then Edit to replace the waveform or
Add to superimpose it. The left panel is the time-domain signal; the
right is the real part of its Fourier transform (fftshifted so zero
frequency is centered), recomputed live with a hand-written radix-2 FFT.
Add a few components to watch a composite signal’s spectrum build up.
Time domainFrequency domain (FFT)
Both live in a private repo, so there's no source link above — these descriptions come from reading the actual code.