Programming
GW-BASIC on a TI in 1986. VB.NET today. I am aware of how that sounds — four decades and I never once left the family. The tools ship, they work, and they are still maintainable, which is more than I can say for some things written in more fashionable languages.
The first real program I had anything to do with, I did not write. I typed it — out of a magazine, the way you did then. Over two thousand lines of BASIC, three months of evenings, character by character.
Then I ran it. It failed. And I could not find out why.
Two thousand lines, no idea which one was wrong, and nothing anywhere in it that said this part, at least, is correct. There was no way in. So I threw it out and started again from the first line — typing a stretch, running it, checking my work, and only then typing the next stretch. It took longer and it worked.
A system you cannot bisect is a system you cannot debug. Two thousand unverified lines is not a program with a bug in it — it is a wall. Every principle further down this page, and most of the ones on the AI sheet, are a fourteen-year-old's workaround that turned out to be the entire method: build a piece, prove it, then build the next piece against something you already trust.
Almost everything I write exists because I did a job twice and objected. Not products — tools. A viewer for ladder logic so it can be read outside the vendor's IDE. A hub that loads games as plug-ins. A memory system so an AI stops starting every conversation from nothing.
The common thread is a contract. Get the interface right and the things behind it become cheap to add. Get it wrong and every addition is a negotiation with the previous one.
The test of a plug-in contract is brutally simple: if adding a new one takes more than one file and one registration, the contract is wrong. Not inconvenient. Wrong. Go back and fix the interface, because every future addition pays that tax again.
BASIC to VB6 to VBScript to VB.NET is not four languages, it is one that kept growing. Every step added something without demanding I throw away what already worked — which is the same bargain I now insist on from architecture: no rewrites, every phase builds to the next.
I did not adopt that principle from a book. I lived it for forty years before I had a name for it, and I would be suspicious of anyone who arrived at it the other way round.
That diagram is a claim. This is the evidence for it: one lobby, six tiles, and every one of them a plug-in the host knows nothing about beyond the interface.

The reason this is a real test rather than a flattering one is how little the six have in common. A rendered wheel with physics and a bet layout. A card game with a shoe and a dealer that has rules. A board game with an opponent that has to think. A grid puzzle with chording. A stat page that reads history instead of playing anything. And a full AD&D crawler with turns, light radius and an inventory.
If one interface holds all of that, the interface is the right shape. If it had needed a special case for any one of them, it would have been wrong and every future game would have paid for it.




The largest plug-in, and the one that stopped being a game and became a systems problem. AD&D 2nd edition rules — THAC0 and all — with light radius burning down in real minutes, encounter distance in feet, and surprise that actually matters.

Look at the monster panel. AC and THAC0 are filled in; hit dice, damage, speed and XP are question marks. You only know what you have earned — the entry fills in as you fight them. The bestiary is the character's, not the player's, and it means the first of anything is genuinely dangerous.


Three details in those two screens say more about how I build than anything I could claim.
Encumbrance is honest. The purse reads 99 pp, 9 sp, 6 cp (2.3 lb) — the coins have weight, because in 2e they do. Light is tracked in real minutes remaining, not in torches. If you're going to implement a rules system, implement the parts that were load-bearing.
The prose does the work the graphics can't. "You pass under the gate of Millbrook. Woodsmoke, wet stone, and somebody shouting about turnips." That is the entire art budget of an ASCII game, spent well.
And it tells you what isn't finished. The class panel reads "Spells arrive with the Phase 6 mining." There's a Kill Character (test) button still sitting on the sheet. The build phases are visible from inside the game — which is either a lack of polish or the most honest possible status report, and I'd argue it's the second one right up until someone else has to play it.
A second cellular automaton, and a considerably stranger one. The simulation is the easy half; the tabs across the top are the actual project — Universe, Observer, Predictions, Narratives.


That distinction is the whole reason it exists. A classifier that only recognizes gliders and blinkers tells you what you already programmed. One that can say "there is a thing here and I don't know what it is" can find something you didn't anticipate — which is the only interesting outcome in a system whose behavior you can't predict from the rules.
And it's the third time on this sheet: the DSP processor watching its own state transitions, the memory substrate on the AI page watching what recurs across sessions, and now an automaton naming its own emergent structures. I did not set out to build the same thing three times. Apparently it's just what I find interesting.


Every parameter is exposed and live, which is the only honest way to build something whose behavior you cannot predict from the rules. You are not designing the output. You are designing the conditions and then finding out.
Every one of these started as a job I did twice and objected to. None of them are products. They exist because the alternative was doing the tedious part again.



SQLAccess opens read-only and you have to deliberately leave that state. The default protects you; the capability is one click away. That's the same instinct as a maintained-contact selector switch — safe unless someone means it.
The largest of them, and the one where the two halves of this site meet. Real-time audio capture and processing, written to scratch the electronics itch with software instead of solder.



Then there's the tab I did not expect to build. Brains — a cognitive layer watching the application: state machine transitions as they happen, a habit-loop report looking for sequences that repeat often enough to count, and an attention spotlight tracking where activity actually went.

Which is the same idea as the memory substrate on the AI sheet, arrived at from the opposite direction. There I wanted a machine to stop forgetting across sessions; here I wanted an application to notice what it kept doing. Both are the instinct that a system which cannot observe itself cannot be improved deliberately — you're left changing things and hoping.
I don't think I noticed they were the same idea until I built the second one.
Sometimes the right move is to write the protocol yourself. This is a hand-rolled I²C master in Propeller assembly, driving an eight-channel device — address byte, then eight register-and-data pairs, every clock edge and every data transition placed by hand.
No peripheral, no library. Two pins, a counter, and the bus specification.
' start condition, then clock out one byte, MSB first
starti2c waitcnt time, off_time
xor OUTA, i2cSDA ' data low while clock high
waitcnt time, off_time
xor OUTA, i2cSDL
AddressB mov OutHolder,address
loop shl OutHolder,#1 ' walk the bit up to position 9
test OutHolder,#%1_0000_0000 wc
muxc OUTA, i2cSDA ' pin follows carry — one instruction
clkloop1 waitcnt time, off_time
xor OUTA, i2cSDL ' toggle clock
xor CkState, #1
waitcnt time, off_time
cmp CkState, #1 wz ' data may only change while clock is low
if_z jmp #clkloop1
djnz idx, #loop ' repeat for each bit in the byte
The test / muxc pair is the nice bit. test drops the selected bit straight into the carry flag, then muxc writes carry onto the pin — one instruction, no branch, no temporary. Shifting the bit up to a fixed position rather than shifting the mask down means the test operand is a constant, which keeps it a single long.
The whole driver is unrolled — seventeen near-identical blocks rather than a loop over a pointer. That reads as repetition and on most platforms it would be. A cog has 496 longs of code space and no stack, so a call costs you self-modifying code; when the routine fits, unrolling buys deterministic timing for space you weren't otherwise going to use.
Eight cores, each with its own memory and its own copy of the code, sharing a hub in strict round-robin. There are no interrupts. If you need something to happen concurrently you don't schedule it, you hand it a core.
Which is why this architecture reads as obvious to anyone who came up on PLCs. A PLC guarantees you a deterministic scan; a cog guarantees you deterministic instruction timing, and waitcnt lets you place an edge to the clock cycle. Both are answering the same question — when exactly does this happen? — and both answer it by refusing to let anything preempt you.
It is the same instinct that says a machine which stops randomly is not random. Determinism isn't a performance feature. It's what makes the thing debuggable.
# the ones that survived contact with production
no rewrites every phase builds to the next. if the next stage
requires tearing out the last one, the last one
had the wrong shape.
explicit > implicit verbosity is not a defect. the thing you left
unsaid because it was obvious is obvious only
to you, and only today.
no parse walls never put queryable data behind a parse. keep the
raw blob as a receipt, shred the rest into columns
you can actually ask questions of.
one job each everything is a tool that supports other tools.
build one thing with a clean edge, then build
the next thing against that edge.


Yes it's a real programming language. It's a domain-specific language with a graphical syntax, hard real-time guarantees and a debugger that shows live state on the running machine. Show me the web framework that does that.
A panorama sweep that assumed the world would hold still. It did not. The algorithm made exactly one bad assumption and then committed to it, with total confidence, all the way across the frame.
Which is the whole argument for stating a tolerance: every measurement carries one, whether or not anybody wrote it down.
SCALE: NONENOT TO BE USED FOR FABRICATION