Try, Don't Trust

Live Demos

Working AI you can poke at, right here in your browser. Nothing is uploaded, nothing is faked. Open devtools and watch it run on your device.

These demos are how I keep the fundamentals sharp. The same rigor, reliability, explainability, and code you can actually run, goes into client work. If something here maps to a problem you have, let's talk.

Live Demo · Glass-Box RAG

Ask My Portfolio Anything

Type a question. An embedding model loads into your browser, reads it, and searches everything on this site semantically. Then, if you want, a small language model runs on your device and writes a grounded answer from the sources it found. Every step is visible, and nothing leaves this page.

The Embedding Space

Ask a question and this becomes a 3D map: every project and article as a point, your question landing next to its nearest neighbors.

projects articles about services your question

384-dimensional embeddings projected to 3D with PCA. Drag to rotate.

Retrieval, measured on 40 labeled queries100% recall@30.96 MRRon-topic 0.24, off-topic 0.16evaluated in CI, not claimed
Under the hood: measured, not vibes
  • Build-time indexing. A pipeline chunks every project, article, and page on this site (79 chunks), embeds them with MiniLM, int8-quantizes the vectors, and ships the whole index as a 115 KB file. The same document-to-vector pipeline you would run against a real corpus, minus the vector database it does not need.
  • On-device retrieval. Your question is embedded by the same model running in your browser via WebAssembly, then scored against every chunk with cosine similarity. No server sees your question.
  • Evaluated before shipping. A 40-question labeled test set measures this exact pipeline: 100% recall@3, mean reciprocal rank 0.958. The first run scored 97.5%; the failing query ("built for utility companies") exposed missing sector vocabulary in the chunks, which was fixed and re-measured. Retrieval evaluation is how RAG systems earn trust.
  • An honest no-match gate. Off-topic questions score below 0.16 against this corpus while on-topic ones score above 0.24, so a 0.20 threshold refuses cleanly instead of dredging up noise. Calibrated from data, not guessed.
  • Retrieval, then generation, both on-device.Retrieval comes first, because a generator is only as good as what you feed it. The optional answer is written by a 0.5B-parameter instruct model (Qwen2.5) running in your browser via WebGPU, or WebAssembly where WebGPU is missing, prompted to use only the retrieved passages and to admit when it cannot answer. No API, no server, no key. That grounding discipline, answer from the sources or refuse, is what keeps a real RAG system from making things up.
Live Demo · How AI Reads

Watch a Tokenizer Learn to Read

A language model does not see words, it sees tokens: subword chunks it learned by byte-pair encoding. Start from single letters, then merge the most common adjacent pair again and again, and frequent pieces like "low" or "est" fuse into single tokens while rare words stay splittable. Here it trains live on a sample text, then splits whatever you type.

Training: the corpus, retokenized as it learns
lower
newer
slowest
fastest
nearest
lowest
vocab 12corpus tokens 91chars/token 1.00
Characters per token, climbing with each merge
Merges learned, in order

starting from characters...

thenewestreader
Under the hood: why models read in subwords
  • The algorithm. Byte-pair encoding was invented as a compression trick (Gage, 1994) and repurposed for language by Sennrich, Haddow, and Birch (2016). Represent every word as a sequence of characters, count how often each adjacent pair occurs across the corpus, merge the single most frequent pair into a new symbol, and repeat. Each merge adds one token to the vocabulary and shrinks the corpus, which you can watch in the counters above.
  • Why not just use words? A word vocabulary can never be complete: new names, typos, code, and other languages would all be unknown. A pure character vocabulary is complete but makes sequences long and meaning thin. Subwords are the compromise: a few tens of thousands of pieces that cover any text, spell common words as one token, and fall back to smaller pieces for rare ones.
  • Lossless by construction.Tokenizing is reversible: concatenate a word's tokens and you get the word back exactly, a property the test here checks on every word in the corpus. That matters, because the model must be able to turn its token predictions back into text without ambiguity.
  • Honest scope.Real tokenizers like GPT's work on raw bytes rather than letters, add a word-boundary marker so a piece at the end of a word differs from the same piece in the middle, and are trained on billions of characters. This demo strips to letters and word-internal merges so the mechanism is visible; the algorithm on screen is the real one.

This is the first thing that happens to your words inside a language model. Next they become vectors, and then they attend to each other.

Live Demo · How AI Reads

See What a Sentence Pays Attention To

Attention is the idea that made modern language models possible: every word gets to look at every other word and decide which ones matter. Type a sentence. A language model runs in your browser to turn each token into a vector, then a self-attention head scores every token against every other and softmaxes it into the weights you see here. Click any row to follow one token's attention.

Pick a sentence above or type your own, then hit Visualize.

Under the hood: softmax(QK^T / sqrt(d)) V, and nothing faked
  • The operation. Self-attention turns each token into a query, a key, and a value. It scores every query against every key with a dot product, scales by the square root of the dimension so the numbers stay tame, softmaxes each row into weights that sum to one, and mixes the values by those weights. That single operation, stacked and repeated, is the transformer (Vaswani et al., 2017).
  • Why it changed everything. Before attention, models read text one step at a time and forgot the beginning by the end. Attention lets every word reach any other word in one hop, in parallel, so long-range meaning survives and training scales. Every large language model in use today is built on it.
  • What you are seeing. Your sentence is embedded by a real model (MiniLM) running on your device, one vector per token. This demo computes one self-attention head over those real vectors, so the weights track the semantic structure the model actually encodes: notice how a pronoun leans toward the noun it refers to. The rows are genuine softmax distributions, non-negative and summing to one, which the tests here check.
  • Where the honesty line is.This is the attention mechanism applied to the model's token embeddings, not a read-out of the model's own internal heads, which its exported browser form does not expose. Same math, real representations, no pretending otherwise.

The model runs on your device, the attention is computed from the vectors it produces, and every weight on screen is a real softmax. Nothing is uploaded.

Live Demo · Computational Biology

How the Leopard Got Its Spots

In 1952 Alan Turing showed that two diffusing chemicals, an activator and an inhibitor, can turn a featureless sheet of cells into spots and stripes on their own. That single idea explains animal coats, coral, and the ridges of your fingerprints. Here it is running live on your GPU. Pick a regime, then click and drag on the canvas to paint fresh activator and watch the pattern heal around your strokes.

Leopard spots · F 0.0300 · k 0.0620click and drag to paint
Where you are in Gray-Scott spacepurple: named presets · amber ring: your F and k (Pearson, 1993)
Under the hood: this is real developmental biology
  • Diffusion-driven instability.Turing's counterintuitive result: diffusion, which normally smooths things out, can instead createstructure when a slow-spreading activator is chased by a fast-spreading inhibitor. Short-range activation plus long-range inhibition is the whole trick. The model here is the Gray-Scott system, an autocatalytic reaction U + 2V → 3V balanced by a feed rate F and a kill rate k.
  • The parameter map is the science.Pearson's 1993 Science paper catalogued how tiny changes in F and k move the system between spots, stripes, mazes, and self-replicating solitons. Drag the two sliders and you are walking that map yourself; the presets are just named coordinates in it.
  • It matches real organisms.Kondo & Miura (Science, 2010) showed zebrafish stripes rearrange exactly as a Turing system predicts. Sheth et al. (Science, 2012) found digit spacing in the limb is set by a Turing mechanism whose wavelength Hox genes tune. Glover et al. (Cell, 2023) traced human fingerprint ridges to a reaction-diffusion system of WNT, BMP, and EDAR signals. The spots you are painting into are the same mathematics.
  • Engineered for the browser.The chemical field lives in a pair of 512×320 floating-point textures. A fragment shader advances 12 Euler steps of the reaction-diffusion PDE per animation frame by ping-ponging between them, a second shader injects activator where you paint, and a third maps concentration through a colour ramp with in-shader bilinear upsampling. No pixel ever leaves the GPU.

Same instinct as the rest of this site: take a deep idea, implement it correctly from the equations up, and make it something you can touch.

Live Demo · Computational Biology

Watch a Protein Fold

A protein is a chain that folds itself into one precise shape, and that shape decides what it does. This is the HP lattice model: each residue is either water-fearing (H) or water-loving (P), and the chain searches for the fold that buries the most H residues together. Simulated annealing drives the hunt. You are watching the hydrophobic collapse that starts every real fold.

H = water-fearing, P = water-loving
Warming up: the chain is about to start its search.
Energy0
Best found0
H-H contacts0
Temperature2.60
Energy over time
H · hydrophobic P · polar buried H-H contact

20 residues · 0 hydrophobic · 0 sweeps

Under the hood: why folding is hard, and why it works anyway
  • One sequence, one shape.Anfinsen's 1973 thermodynamic hypothesis: a protein's amino-acid sequence alone encodes its folded structure, which sits at the free-energy minimum. The HP model keeps only the single dominant force, the hydrophobic effect, and still reproduces cores, surfaces, and folding cooperativity (Dill, 1985; Lau & Dill, 1989).
  • Levinthal's paradox. A 100-residue chain has more possible shapes than there are atoms in the universe, so a protein cannot find its fold by trying them all, yet real proteins fold in microseconds. The resolution is a funneled energy landscape: each favorable contact steers the search downhill, which is exactly what the annealing here is exploiting.
  • The search. Metropolis Monte Carlo over the standard lattice move set (end moves, corner flips, crankshafts) proposes small changes and accepts energy-lowering ones always, energy-raising ones with probability e^(-dE/T). Simulated annealing cools T from hot to cold so the chain first explores widely, then settles into a deep minimum. Every conformation stays a valid self-avoiding walk.
  • A parity surprise.The square lattice is bipartite, like a checkerboard, so every step flips colour and a residue's colour is fixed by whether its sequence position is even or odd. Two residues can only touch when they sit on opposite colours, meaning their positions differ by an odd number. In a strictly alternating H/P sequence every H sits at an even position, so any two H's differ by an even number and can never touch, giving exactly zero contacts no matter how it folds. The alphabet, not just the search, sets the ceiling.
  • It is genuinely hard.Finding the true lowest-energy fold in the HP model is NP-hard, proven for both the 2D and 3D lattices (Crescenzi et al., 1998; Berger & Leighton, 1998). So this demo does not promise the global optimum; it shows the same heuristic search that real structure-prediction pipelines lean on, with the energy trace as honest evidence of progress.

The energy is computed from the equations, the search is a real optimizer, and the number on screen is what it actually found. No shortcuts.

Live Demo · Computational Biology

Line Up Two Sequences

How do you tell whether two genes or proteins are related? You line them up so the matches stack and the mutations and insertions cost you points. The two classic algorithms do this exactly, not by guessing, by filling a grid where every cell is the best score reachable there. Watch the matrix compute, then watch the optimal path trace back.

Paste your own · letters only, up to 18 each
Alignment score13Smith-Waterman (local)
high score penalty optimal path
GTT-AC||| ||GTTGAC
Under the hood: one recurrence, two famous algorithms
  • The recurrence. Each cell asks a single question: is it better to align these two letters (move diagonally and add a match or mismatch score), or to open a gap (move up or left and pay the gap penalty)? Take the best of the three. That local choice, filled across the whole grid, is guaranteed to find the globally optimal alignment. This is dynamic programming in its purest form.
  • Global vs local. Needleman-Wunsch (1970) forces the path from corner to corner, aligning the sequences in full. Smith-Waterman (1981) adds one rule, never let a cell go below zero, and starts the traceback from the highest cell, so it finds the best matching sub-region instead. One extra max() turns global into local. Toggle the modes and watch where the path starts.
  • Why it matters. Alignment is how we measure homology, spot mutations, and place reads on a genome. The score is a real number you can rank and threshold. Change the match, mismatch, and gap costs and the optimal alignment shifts, which is exactly why choosing a scoring scheme is a modelling decision, not a detail.
  • Beyond the toy.Real tools use position-specific substitution matrices like BLOSUM and PAM rather than a flat match/mismatch, and affine gap costs (a large open penalty plus a small extend penalty) via Gotoh's 1982 method, because one long indel is more likely than many short ones. The grid you see here is still the exact engine underneath all of them, running in O(m times n) time.

The matrix is computed from the recurrence, the traceback is the true optimal path, and the score is exact. Change the costs and the math re-derives itself.

Live Demo · Computational Biology

Fire a Neuron

This is the exact model Hodgkin and Huxley built in 1952 to explain how nerves fire, four coupled differential equations solved in your browser. Turn up the injected current. Below a threshold the membrane just leaks back to rest. Cross it and the sodium channels avalanche open, the voltage spikes past zero, and potassium slams it back down. That spike is an action potential.

Tip: click anywhere on the voltage trace to inject current. Click high for a strong jolt that fires a spike, low for a sub-threshold nudge that just leaks away.

At rest near -65 mV. Inject current to cross threshold.
Membrane V-65.0 mV
Firing rate0 Hz

Nudge it up slowly. Somewhere around 6 the cell switches from silent to firing over and over, and firing faster as you push harder.

membrane voltage m · Na activation h · Na inactivation n · K activation
State space · V vs neach spike traces a loop, rest is a point
Under the hood: four equations that explain every spike
  • The membrane is a capacitor with leaky, voltage-gated resistors.One equation tracks the voltage as injected current charges the membrane against three ionic currents: sodium, potassium, and a passive leak. Each channel's conductance depends on the voltage, which is what makes the system nonlinear and excitable.
  • Three gates, three more equations. Sodium opens fast (m) but then inactivates (h); potassium opens slowly (n). The fast positive feedback of m against the slower brakes of h and n is exactly what produces an all-or-nothing spike followed by a refractory pause. Watch m jump first, then h fall and n rise to end each spike.
  • Threshold and frequency are emergent. Nobody codes in a threshold; it falls out of the dynamics. Below a critical current the fixed point is stable and the cell is silent. Above it, a limit cycle appears and the neuron fires repetitively, faster with more current. Slide the current across that point and you can feel the bifurcation.
  • Solved honestly. The four equations are integrated with a forward-Euler step of 0.01 ms using the original 1952 rate functions (with the removable 0/0 points handled analytically). No lookup tables, no faked spikes; if you find a spike on screen, the math produced it.

The same equations that won a Nobel Prize in 1963, running at 60 frames a second on your device.

Live Demo · Computational Biology

Watch Evolution Roll the Dice

Evolution is not only survival of the fittest. In any finite population, pure chance also decides which genes survive, an effect called genetic drift. Here are dozens of identical populations evolving in parallel under the Wright-Fisher model. Turn selection off and watch chance alone split them between fixation and loss. Turn it up and watch a favored gene sweep.

Tip: click any trajectory to follow that one population's lineage, and watch where chance takes it. Click empty space to let go.

Pure drift: with no selection, chance alone decides each fate.
Generation0
Mean freq0.50
Fixed / lost0 / 0
Heterozygosity0.00
still segregating fixed (reached 100%) lost (reached 0%) mean across populations neutral expectation (drift)
Under the hood: chance is a force of evolution too
  • The model. The Wright-Fisher model is the standard idealization of a finite population. Each generation of 2N gene copies is drawn by sampling from the current allele frequency, so the next frequency is a binomial random variable. Selection tilts the sampling odds toward the favored allele, mutation flips a small fraction of copies, and everything else is chance.
  • Drift is not noise, it is a force. With selection off, the mean frequency across populations stays put, but each individual population wanders until it hits 0 or 1 and sticks. The probability a neutral allele eventually fixes is exactly its starting frequency, which you can read off the demo: set selection to zero and the fraction of populations that fix matches the start frequency slider.
  • Smaller populations lose variation faster. The heterozygosity readout, the chance that two random copies differ, decays at a rate set by the population size (about 1 over 2N per generation). Shrink N and watch the populations fix quickly and the variation collapse. This is why small and bottlenecked populations lose diversity, a central concern in conservation genetics.
  • Selection versus drift. A beneficial allele is not guaranteed to win. Fixation probability rises with the selection coefficient but stays well below one for weak selection in a small population, which is the tension Fisher (1930), Wright (1931), and later Kimura formalized. Nudge s upward and watch the cloud bend from a random spread into a confident sweep.

Real binomial sampling, real selection and mutation, a seeded generator so a given setting always plays out the same way. The dice are honest.

Live Demo · Generative Models

Watch Noise Become a Shape

This is how image generators actually work, shrunk to two dimensions so it runs in your tab. A cloud of pure noise is denoised one step at a time until it lands on a target. Each step asks the same question a diffusion model asks: given this noisy point, what did the clean data probably look like?

Denoising0%
Noise level t80/80
Cloud spread1.00
Spread over time
denoising particles target the noise is pulled toward score field: where the denoiser pushes

0 particles · 80-step schedule · exact DDPM sampler

A note on compute. This is deliberately a two-dimensional toy so it stays smooth in a browser. Real image diffusion (Stable Diffusion, DALL-E, Imagen) runs this same reverse process over millions of pixels with a large neural network at every step, which needs a GPU and seconds to minutes per image. The math on screen is the real thing; only the scale is small. On an older laptop this demo does genuine per-particle work each frame, so if the fan spins up, hit Pause.

Under the hood: the reverse process, and where the neural net normally goes
  • Forward is easy, reverse is the trick. Adding noise to data is trivial: after enough steps anything becomes a formless Gaussian blob. Generation runs that film backward. Sohl-Dickstein et al. (2015) showed that if you can undo one small step of noising, you can walk all the way from noise back to data.
  • Each step predicts the clean data. The optimal denoiser at noise level t is the posterior mean E[x0 | xt], a weighted average of the possible clean points, with nearer points weighted more heavily. We take that estimate, step toward it, and add back a precise amount of noise. That is exactly the DDPM update of Ho, Jain and Abbeel (2020).
  • The score, in closed form here. Because our target is a fixed set of points, the noised distribution is a mixture of Gaussians, so its gradient, the score that points toward data, is a formula, not a guess. Song and Ermon (2019) and Song et al. (2021) showed diffusion is equivalent to learning this score.
  • Where the U-Net lives. Swap our point cloud for the set of all natural images and the score has no formula. That is the one and only job of the giant network in a real diffusion model: estimate this score from data. Everything else you are watching, the schedule, the posterior step, the added noise, stays the same.
  • Watch the spread. The trace tracks how tightly the cloud is packed. It starts wide as pure noise and shrinks as particles commit to the shape, a direct, honest readout that generation is converging rather than a canned animation.

The schedule, the posterior mean, and the noise added at each step are computed from the DDPM equations. The particles land where the math sends them. No pre-rendered frames.

Live Demo · The Signal Microscope

Watch Sound Become Numbers

Every stage of the audio pipeline behind the cough monitor, animated live: raw signal, Hamming window, hand-written FFT, mel filterbank, MFCC fingerprint. Pick a synthesized sound, or turn on your microphone and watch your own voice flow through the math.

Spectrogram · frequency over time
1 · Raw waveform
2 · Hamming window tames the edges
3 · FFT spectrum (the 40-line, parity-tested one)
4 · 26 mel filters, hearing-shaped
5 · MFCC fingerprint, the barcode of sound

Same hand-written DSP that powers the cough baseline monitor, verified against a reference DFT to 1e-14. Audio never leaves your device.

Live Demo · Personal Health Baseline

Your Cough Has a Fingerprint.

Record your healthy cough a few times to build a personal acoustic baseline. Later, the monitor scores how far a new cough deviates from it: not against a population model, against you. The signal processing (FFT, mel filterbank, MFCC) is hand-rolled and runs on-device.

Build Your Baseline
0/3 healthy coughs recorded

Cough naturally into your microphone. The recorder captures 2 seconds and finds the cough automatically.

🔒 Audio is processed and stored only on your device.

Acoustic Fingerprints
Baseline (latest)No baseline yet
Latest checkNo check yet

Concept demonstration, not a medical device and not medical advice. Mel-frequency analysis: 26 filters · 12 MFCCs · hand-written FFT, verified against a reference DFT.

📝 Read the build deep-dive on Medium →

Live Demo · Assistive Tech Preview

Mirror Therapy, Without the Mirror Box

Mirror-box therapy reduces phantom limb pain by showing amputees their missing limb moving again. This preview recreates that illusion with hand tracking: show one hand, and its phantom twin moves on the other side.

🔒 Runs entirely on your device. Video never leaves your browser.

Try These
  • 01Open and close your fist, slowly
  • 02Touch each fingertip to your thumb
  • 03Rotate your wrist in small circles
  • 04Spread your fingers wide, then relax
The Science

Mirror therapy (Ramachandran, 1990s) exploits visual feedback: seeing the "missing" limb move can reduce phantom pain. Hand tracking removes the physical mirror box, making the therapy portable and measurable. The full WebXR version is in development.

Concept demonstration of the interaction, not a medical device and not medical advice.

📝 Read the build deep-dive on Medium →

Live Demo · Adversarial Evaluation

Can an AI Spot You?

Modern surveillance is automated, so test against the actual adversary. Upload a photo (camouflage, hunting gear, or just you in the garden) and an object-detection model hunts for you at four simulated distances.

🔒 Images are analyzed on your device. Nothing is uploaded.

…or drag & drop an image here
Detection Report

Add a photo to generate a detection range profile.

Adversary model: COCO-SSD (pretrained, Google) running on-device via TensorFlow.js, a single-model preview of the full multi-model ensemble concept. Distance is simulated by reducing pixels-on-target.

📝 Read the build deep-dive on Medium →

Live Demo · Keypoints to Measurements

21 Keypoints. Every Frame. Your Device.

Keypoints only matter once they become measurements. This demo tracks 21 landmarks per hand and turns them into numbers: the real-world gap between your thumb and index finger (the amber ruler on screen), how many fingers you're holding up, and how open your hand is. That's the same keypoints-to-measurements principle behind my utility pole attachment-height and clearance work. Try pinching slowly.

🔒 Runs entirely on your device. Video never leaves your browser, nothing is recorded or uploaded.

Detections

Start the camera to see live detections.

21 keypoints per hand·7 gesture classes·GPU-accelerated in your browser·zero frames uploaded

Under the hood: what's actually running
  • A three-stage vision pipeline: a palm-detection model locates hands in the frame, a landmark model regresses 21 3D keypoints per hand, and a gesture classifier runs on top of the landmark geometry.
  • The models are Google's MediaPipe(float16-quantized), executed in-browser through WebAssembly with a GPU delegate. Credit where due: I didn't train these.
  • My work is the engineering around them: lazy loading so nothing downloads until you opt in, the render loop and overlay, throttled UI updates, and clean camera lifecycle.
  • Knowing when to fine-tune insteadis the real skill: for utility-infrastructure keypoints, off-the-shelf models weren't enough. See the custom fine-tuned keypoint model I built for that.

📝 Read the build deep-dive on Medium →

Live Demo · No Smoke, No Mirrors

Draw a Digit. Watch a Neural Net Think.

A neural network I trained from scratch with just Python and math, no ML frameworks, compressed to 145 KB and running in your browser right now as pure JavaScript. No libraries, no GPU, no API calls.

Your Canvas
✏️ Draw a digit (0–9) here
Inside the Network
input 28×28
dense 128 · relu
dense 64 · relu
0123456789
softmax
Prediction
·waiting for ink…
0
1
2
3
4
5
6
7
8
9
Why this digit? Pixel influence
Draw a digit to see which strokes the network leans on.

109,386 parameters·98.2% test accuracy·int8-quantized·zero dependencies

How this was built: see the actual code
  • Trained from scratch in raw NumPy: hand-written forward/backward passes and Adam optimizer, no ML framework. Shift augmentation makes it tolerant of off-center drawings. train_digit_model.py →
  • Compressed for the web: weights int8-quantized (~4× smaller) with no measurable accuracy loss, shipped as a 145 KB JSON file inside this page.
  • Inference is ~80 lines of plain JavaScript: the matrix math runs right here, no TensorFlow.js, no API. inference.js →
  • Verified, not vibes: a parity test asserts the JS engine reproduces the Python model's probabilities to within 1e-6. test_inference_parity.mjs →
  • It shows its reasoning: the saliency map is the exact gradient of the winning digit's score with respect to every pixel (Simonyan, Vedaldi & Zisserman, 2013), backpropagated through these same weights and multiplied by the ink that's actually there. Red strokes raised that score, blue lowered it. Interpretability, not a decorative heatmap.

📝 Read the build deep-dive on Medium →

You just watched it run on your own machine

Want this kind of rigor on your problem?

Every demo here is honest, in-browser, and open to inspection. I bring the same standard to client work. Tell me what you're trying to build.