#Started thinking more about dimensions and stuff
##All I did was checking the measurements so I will attach a photo of the same acrylic panel
#Started thinking more about dimensions and stuff
##All I did was checking the measurements so I will attach a photo of the same acrylic panel
I wanted a Raspberry Pi display that doesn’t look like a normal monitor: an image that floats on an acrylic board. My first plan was the obvious one: point a projector down at an 880 x 300 mm acrylic board and call it a day.
That fell apart fast. Clear acrylic doesn’t show a projected image because the light goes straight through, so I’d need frosted or white acrylic. The board’s shape was also a problem. 880 x 300 is about 2.9:1, while projectors output 16:9, so around 40% of the pixels would land off the board. And a normal projector needs to sit about a meter above it.
So I switched to a Pepper’s ghost setup: a hidden screen above, and clear acrylic at 45° that reflects it toward you, so the image looks like it’s floating. Much cooler. The catch is that the floating image is exactly the size of the screen, so filling the board meant a huge, heavy, expensive monitor hanging overhead. Not happening on a Hack Club budget.
What I actually wanted was to take a small screen and magnify it. I looked at a Fresnel lens over a screen, but the physics doesn’t work: a magnifier only makes the screen look bigger from one spot. It can’t put a sharp image onto a surface. The only thing that does that is a projector. So instead of buying one, I’m building one from parts:
The Pi just sees it as an HDMI monitor.
The hardest part so far was the budget. Hack Club grants are capped at $600 and you only get one submission, so everything had to fit now. My first parts list came to $661, before shipping. I trimmed it line by line:
Then I checked every link and price, and found the Pi 5 had gotten a lot more expensive this year because of the RAM shortage. More rebalancing, but the final list fits.
Next: CAD for the ghost box.
Last time I had a bigram model trained on The Wizard of Oz that mostly made gibberish. Since then ARIA has gone through three versions, and I ended up writing most of the pipeline from scratch.
I replaced the bigram with a character-level GPT: 8 layers, 512-dim embeddings, 8 attention heads, 256-character context, trained on web text on my RTX 5060 Ti.
block_size from 8 to 256 so the model can actually see whole sentences-batch_size flag secretly setting block_size, and eval running 500 batches every 250 stepsFinal pretraining loss was about 1.30 per character, and it started writing real English sentences (that didn’t make sense yet).
The base model only continues text, so I fine-tuned it on Alpaca formatted as User: ... / ARIA: ... conversations, with a lower learning rate so it wouldn’t forget English. Loss went down to about 0.91.
Then I had to fix the chat script: wrapping prompts in the right format, cutting replies at the end of a turn, and adding temperature + top-k sampling so it stopped picking random characters. First real reply to “How do you make a cookie?”:
You make a cookie are both a cookie.
Grammatically broken, but it’s an actual answer, in the right place, that stops on its own. The pipeline works.
The biggest limit was being character-level: the model wasted most of its capacity on spelling, and 256 characters isn’t much context. So I wrote a byte-level BPE tokenizer from scratch instead of using a library:
get_stats and merge to find and merge the most common pairsencode / decode with a round-trip test, plus save/load for the merges"is to " and "00 MPa ")encode that made it about 2.3x faster on small text and much more on big filesTrained on about 1MB of educational text plus chat data, with 2048 tokens. It learned real word pieces like " economic", " School" and "rying", and gets about 3x compression. Since it’s byte-level, there are no unknown characters anymore, so Korean works too.
I encoded 500MB of fineweb-edu into 169.6M tokens, saved as uint16 binary and loaded with np.memmap. Then I rewrote the model:
F.scaled_dot_product_attention instead of 8 separate heads, about 3x faster per stepstate_dict checkpoints with optimizer state and step count, so a crash doesn’t wipe a runTest run: loss started at 7.71 (right at ln(2048), which is what random guessing should give) and reached 4.27 after 1000 steps, with train and test matching the whole time.
August 17, 2026 · 11 hours, one sitting
I finished Ocean Board today.
Ocean Board is a custom devboard I designed around the RP2040 — my own take on what a Pico-style board should look like, built for the stuff I actually want to do with it instead of whatever the off-the-shelf version assumes. I sat down and just didn’t stop until it was done. Eleven hours later, it’s real.
I wanted a board that was mine — something I understood down to every trace and part choice, not just another Pico clone I bought off a shelf. RP2040s are everywhere, but building my own version meant I got to decide exactly what went on it and why.
Honestly it was one of those sessions where I sat down expecting to make a dent and just… didn’t stop. Nailing down the BOM, double-checking footprints, assembly, and then the moment that actually matters — powering it on for the first time and finding out if eleven hours of work was going to boot or smoke.
(This is the part I want you to fill in — what actually went wrong, what almost didn’t work, the moment it powered on, anything you had to re-solder or debug. That’s the stuff that makes a devlog feel real instead of a spec sheet.)
Eleven hours is a long single sitting — next time I should actually track where the time goes. Also already have a BOM scaled up for a 10-board batch if I ever want to build more than one of these.
More devlogs coming as I start actually building on top of this thing.
Got the Neural_network class doing an actual feedforward pass now, but honestly most of my time this session wasn’t spent typing, it was spent on a whiteboard (well, my head) trying to actually understand the math before I wrote a single line.
Matrix multiplication only works if the columns of the first matrix match the rows of the second, and figuring out WHY that has to be true, not just memorizing it, is what ate most of my time. Once that clicked, the actual network made a lot more sense: weights between input and hidden layer have to be shaped (hidden_nodes, input_nodes) specifically so the multiplication lines up correctly, same for hidden to output. It’s not arbitrary, the shape IS the math.
feedforward(input): turn the input into a matrix, multiply by weights, add bias, sigmoid, repeat for the next layer, turn the result back into a normal arrayFed it [1, 0] and got a real guess back out. Random weights so the guess means nothing yet, but the math pipeline actually works end to end now, and I understand every step of it instead of just copying it.
Weights used to randomize as integers 0-9 which is wrong for a neural network, changed it to floats between -1 and 1, which is what weights are actually supposed to start as.
train() exists but does nothing yet. That’s the next math rabbit hole, figuring out how to actually adjust these weights based on how wrong the guess was.
Slower progress than usual but way more understanding behind it. Thanks for reading, see you next devlog!
(I am posting a picture of my dog since I have nothing to post a picture of, this is probably the devlog with the most pictures!)
Wired the Neural_network class up to a real feedforward pass. Before this it was just remembering how many nodes it had — now it actually takes an input and produces a guess.
feedforward(input) now does the full pass: multiply by weights, add the bias, squash through sigmoid, twice (once per layer), and hands back a real guessRan it with a plain input array [1, 0] and got an actual output back. First time this thing has done anything resembling thinking, even if the “thinking” is currently just random weights.
Matrix.make_from_array() turns a plain array into a column matrixmake_to_array() turns a matrix back into a plain arrayNeeded both since the network has to convert real inputs into matrix form to do the math, then convert the result back into something normal code can use.
randomize_matrix() used to fill values with random integers 0-9, which makes no sense for neural network weights. Changed it to random floats between -1 and 1, which is the actual range weights should start in.
train() exists but it’s empty right now — the network can guess, it just can’t learn from being wrong yet. That’s next.
Genuinely exciting one, this is the first time the neural network side of the project has produced output instead of just being scaffolding. Thanks for reading, see you in the next one!
I went back through my Matrix class and actually fixed a bug that was sitting in the matrix multiplication code — I was resetting my running sum inside the innermost loop instead of before it, so it was only ever keeping the last multiplication instead of adding them all up. Classic off-by-scope mistake.
scalar_mult and made it its own static matrix_mult function, since cramming two different operations into one method never made much sensemap and map_index
map(fx) runs a function over every value in the matrixmap_index(fx) does the same but also passes in the row/column, so the function can behave differently depending on positionmap is forprint_table
console.table(this.matrix_values) everywhereFeels like boring plumbing work, but this is the stuff the whole neural network is going to be built on top of. I’d rather get it solid now than debug it later once there’s an actual brain depending on it.
Thanks for reading, see you in the next one!
What’s up guys! Quick but important update this time.
I leveled up from just a single perceptron to actually starting a real multi-layer neural network. First thing I had to do for that was build my own Matrix class, since it turns out neural networks are basically just matrix math wearing a trench coat — the weights, the inputs, the outputs, all matrices, and “guessing” is really just multiplying them together.
So I wrote one from scratch. It can zero itself out, randomize itself, transpose, multiply by a number or by another matrix, and add stuff to itself. Not the most exciting thing to build, but literally every neural network sits on top of this, so I wanted to actually get it instead of just grabbing a library and moving on.
I also started the real Neural_network class, but honestly it’s not doing much yet — right now it just remembers how many input, hidden, and output nodes it has. No actual thinking happening in there yet. Next up is hooking the Matrix class into it so it can take inputs and spit out a real guess, and after that, actually learn from being wrong.
Now that I’ve got two real tracks going I split the project up — Genetic_Algorithms/ for the ecosystem sim, and NeuralNetwork/ for all this neuroevolution groundwork.
Slow progress this time, but it’s the kind of slow that matters — I’d rather actually understand the math than fake my way through it. Thanks for reading, see you in the next one!
A from-scratch FPV drone, small enough to wear on a necklace with a magnetic quick-release mount. FPV camera now, AI vision later. Building it in phases so I actually get it flying before piling on features.
Motor: BETAFPV 1103, 15000KV (x4) - 1.5mm shaft, M1.6 mount on 8.5mm pattern
Link: https://betafpv.com/products/1103-brushless-motors
FC: GEPRC TAKER F411-12A-E (1-2S AIO), 25.5x25.5mm M2 mount
Link: https://geprc.com/product/gep-taker-f411-12a-e-12s-aio/
Props: Gemfan 1635-3 40mm Tri-Blade, Clear Blue - 1.5mm shaft
Link: https://wrekd.com/products/gemfan-1635-3-31mm-triblade-1mm-shaft
Frame: Custom, Fusion 360
Full CSV: drone_bom_phase1.csv
Watch out for:
Phase 3 - FPV + control:
Caddx Ant camera (16:9 black), BETAFPV M03 VTX (connector compatibility with the camera still unconfirmed), iFlight 1S 300mAh battery, BETAFPV BT2.0 charger, RadioMaster Zorro, goggles TBD
Phase 2 - AI:
Grove Vision AI V2 + XIAO ESP32S3 Sense (both from icbanq), CSI camera for the Vision module not yet picked
Phase 4 - Wearable:
N52 magnets, necklace hardware, PETG-CF for the final frame revision
I am back with a new project! And it is not small…