Owen L
[SLOT: portrait.jpg]
Owen L
[ 2026 ]

I like making things I can pick up and hold. Every tool here I learned because something I wanted to build needed it, and most of my hobbies started the same way.

discord
offline
spotify
not listening right now
currently
reading into the math under ML
tools
Fusion 360 · KiCad · Python · Java
based in
bay area, california
loading model — steno-keyboard.3mf
steno keyboard — CAD, live in browser
drag to orbit · scroll to zoom
steno keyboard, full assembly. orbit to inspect.
projects

five builds, all finished. two with printed parts, three pure software.

The same steno keyboard at a low three-quarter angle, showing keycap height and the rust-coloured switches under the caps
[SLOT: assets/img/cable-view.jpg]
from the side. the two key heights are the layout, not decoration.
Bare 2-layer PCB with 25 switches seated and a Raspberry Pi Pico 2 W hand-soldered at centre; silkscreen reads Steno Mk. 1 by Owen
[SLOT: assets/img/without-case.jpg]
case off. board from JLCPCB, Pico 2 W soldered by hand.
Six printed keycap shells laid in a row, upside down, showing successive revisions of the internal gripping fingers
[SLOT: assets/img/keycap-iteration.jpg]
keycap revisions, upside down. the fingers inside are what kept breaking.

what i was trying to do

Steno key geometry is nothing like a standard board, so I modelled all 25 keycaps from scratch in two profiles rather than adapting an existing set.

what broke

The fingers inside the keycaps kept snapping. I kept answering that by making them thicker, and they kept breaking, until on the fifth revision I stopped arguing with the material and designed them to flex the way PLA already wants to. They held. The case took two revisions. The PCB worked on the first.

where i am now

It works, and it was the first thing I designed where the mechanical, electrical, and software sides all had to agree with each other. Learning to actually write steno on it is the part I have not done yet.

repo: CAD, board files, firmware, photos ↗
[SLOT: assets/video/cg50-demo.mp4]
the calculator playing back a real clip, decoded frame by frame into VRAM.
The video player's info screen on the calculator, listing frame count, resolution, FPS and scale above the prompt EXE: Play Video
[SLOT: assets/img/cg50-menu.jpg]
the add-in reads the header and tells you what it found before playing.

why hack a calculator

Graphing calculators don't play video. I looked for something that already did this on the CG50 and didn't find one, only a project built to loop a single specific clip. So I built the general version: any video in, as long as it fits.

the encoder

A Python script over OpenCV. Every frame is quantised to one of a handful of palettes — 16-colour, 4-level Game Boy grey, 16-level grey, or 1-bit black and white, with room to add more — by nearest colour in squared BGR distance, converted to RGB565, then run-length encoded into (run, palette index) byte pairs.

the container

There was no format to target, so I defined one. A big-endian header of frame count, width, height, fps and palette size, then the palette itself, then every frame length-prefixed so the player can skip a corrupt one instead of losing sync for the rest of the video.

playback

A C add-in against libfxcg. It picks the largest whole-number scale that still fits 384×216, centres the image, and decodes runs directly into VRAM rather than through a drawing call per pixel. Frame timing comes off the calculator's real-time clock, and the same wait polls for MENU or EXIT so you can quit mid-video.

repo: encoder, add-in, Makefile ↗
Minesweeper running in JavaFX — Game, Options and Help menus, mine counter, smiley reset button, elapsed timer, and a 9 by 9 grid of unrevealed tiles
[SLOT: assets/img/minesweeper.png]
running. menu bar, mine counter, and elapsed timer all live.

what it is

A full reimplementation of the original Minesweeper in JavaFX, down to the mine counter, the smiley reset, the elapsed timer, and a working menu bar. Six classes, roughly a thousand lines, built as a school project over three days.

what i had to work out

The assignment was open-ended, so the class structure and the game logic were mine to design rather than follow. Matching the original also meant reconstructing behaviour from how the game plays, since there is no spec to build against, only the thing itself.

what it is

A Raspberry Pi 5, a camera module, an app to drive it, and a case I designed in Fusion 360 and printed. It does what a handheld camera does, and every part of it is something I put there.

why it matters

This is where the pattern started: model the housing, wire the hardware, write the software, find out what breaks. The steno keyboard is the same loop with harder tolerances and a board I laid out myself.

Line plot titled Samples for apple — 45 overlaid audio envelope curves, aligned so the spoken word occupies the same window in each
[SLOT: assets/img/voice-envelopes.png]
45 recordings of “apple”, enveloped and aligned. this is what the model sees.
3D scatter plot of training data — four coloured clusters for apple, banana, mango and pear, each with a star marking its centroid
[SLOT: assets/img/voice-clusters-train.png]
training data after PCA. stars are the centroids.
3D scatter plot of held-out test data projected onto the same axes, showing looser clusters around the same four centroids
[SLOT: assets/img/voice-clusters-test.png]
held-out test data on the same axes. the four clusters still separate.

the pipeline

Two-second clips at 32 kHz, bandpassed to 80–3400 Hz to throw away everything that is not a voice. Each clip becomes an envelope: 64-sample chunks, demeaned, absolute values summed. That envelope gets aligned on a loudness threshold so every word starts in the same place, and SVD reduces it to three dimensions.

what i wrote

The centroid finder and the classifier: given a new point in the reduced space, walk all four centroids, take the straight-line distance to each, return the nearest. I also chose which four of the six recorded words to keep. Apple, banana, mango and pear separate cleanly because they do not share a stress pattern.

where the errors come from

Mango scores lowest at 78.6%. The model is matching the emphasis pattern of the syllables, so words that stress the same way land near each other in the reduced space. Nearest-centroid always returns an answer, which means a word it never trained on still gets assigned to whichever centroid has the closest peaks.

Per-word accuracy on 14 held-out clips each: apple 92.9%, banana 85.7%, mango 78.6%, pear 92.9%.

toolkit

thirteen tools. what I built with each, and where each one came from.

group by

mechanical2

Fusion 360
since mar 2025
~10 parts. Steno Mk. 1 was the largest: 25 keycaps in two profiles, plus case and plate.
self-taught, from Fusion's own tutorials and printing what I modelled.
parametric sketchesjoints3mf export
3D printing
since mar 2025
Every physical part on this page. 25 keycaps across five revisions.
self-taught, alongside the CAD.
PLA for prototypesPETG under load

electrical2

KiCad
since mar 2026
The Steno Mk. 1 PCB. Two layers, fabricated by JLCPCB. The first revision worked.
taught by my dad, hands-on.
schematic2-layer layoutfab export
Hand assembly
since mar 2026
25 switches and a Raspberry Pi Pico 2 W soldered by hand onto the steno board.
self-taught, on the board itself.
solderingthrough-hole

software7

Java
since sep 2025
Minesweeper in JavaFX: 6 classes, ~1,000 lines. Also my USACO language.
coursework. USACO Bronze prep since apr 2026, on top of practice since 2025.
javafxusaco bronzeAP CS A, 2026–27
Python
since nov 2025
The voice classifier above, and the OpenCV encoder behind the CG50 player.
coursework, nov 2025 to jan 2026. Digging into the math underneath since.
numpyopencvmatplotlib
C
since feb 2026
The CG50 player add-in: 215 lines against libfxcg, decoding frames straight into the calculator's VRAM.
self-taught, from the libfxcg headers and the bad-apple-cg50 project.
libfxcgdirect vram writesCG50-VideoPlayer repo ↗
Machine learning
jan — may 2026
The PCA and nearest-centroid work behind the voice project above.
coursework, finished may 2026.
pca / svdnumpy
MicroPython
since mar 2026
The steno firmware: 150 lines, written from scratch rather than forked from QMK or ZMK.
self-taught, on top of Python.
raspberry pi pico 2 wsteno-keyboard repo ↗
Git & GitHub
every project here
Version control on every software project on this page.
self-taught.
github.com/obfuscated-tm ↗
Luau — Roblox Studio
jun — aug 2025
Scripting and Studio modelling. Nothing published; moved on to hardware.
self-taught, from YouTube tutorials.
no longer active

teaching2

Tutoring — CAD & code
robotics course
Helped classmates in an out-of-school robotics course with CAD, code, and physical assembly.
taught others.
cadcodeassembly
Writing lab
2026–27
Tutoring other students on their writing. English, not engineering, and the only entry on this page that is not STEM.
taught others.
englishoutside stemupcoming
about
outside of building
Cycling and weight training.
contact

for freelance work, questions about anything on this page, or anything else.