ZPU on the Web Putting a Firmware Updater in the Browser
Some History First
Before I get into what we just shipped, here's where ZPU actually came from, because it's a better story than you'd expect for a tool whose entire job is to push bytes down a serial cable.
The first version of ZPU wasn't ours. It predates Z Automotive in its current form. It was written for another company called Costar by a Russian developer who happened to be a coworker of the Joe Zizzadoro, who later started Z Automotive Technologies. I have never met the guy. I've stared at his protocol for dozens of hours though, and honestly, respect. The bootloader handshake, the block write flash format, the way addresses advance word by word, all of it has held up for the better part of two decades. The wire format he came up with back then is still the wire format we speak today. If he's reading this somehow, hi, your code outlived a lot of computers.
Years later, when the Mac side of things needed a client, that landed on a 25 year old named Matt West, in 2017. That kid was me. I'd been writing software professionally for about nine months. I knew Objective C the way you "know" a language when you've shipped exactly zero apps in it. I had never touched a serial port in my life. I figured out the protocol by reading the existing Windows source, opening Xcode, and trial and erroring my way through a POSIX serial wrapper until things stopped exploding. The Mac ZPU client that's been distributed by us for the last nine years is structurally the same code I wrote during a string of late nights with too much monster and too much ego to give up.
"At least this will never have to run in a browser. Browsers can't talk to hardware. Thank god.
At some point during all that, I want to say it was the 50th or 100th time I'd debugged a serial timeout bug that turned out to be macOS scheduling something inconvenient, I remember thinking very clearly: at least this will never have to run in a browser. Browsers can't talk to hardware. Thank god.
Anyway. Browsers can talk to hardware now. Lol.
Web ZPU Is Live
Today I'm launching Web ZPU. Full firmware updater, in your browser, no install. Open the page, plug in your device, click connect. Same protocol the original Russian developer wrote. Same write and verify pipeline I ported to Mac when I was 25. Now with zero installer, zero OS lock in, and hopefully zero "please install .NET" support emails. As far as I can tell, nobody else in the aftermarket performance space has shipped a complete firmware programmer on the open web. We're the first.

Why It's Possible Now
There's a browser API called Web Serial. It landed in Chromium based browsers a while back and it does exactly what it sounds like. The page asks for permission, the user picks a port from a dialog the browser controls, and from there you've got a read stream and a write stream. The bytes you push out the writer are the same bytes any other serial library on any other platform pushes out. There's no magic, no shim, no electron build, none of that.
When I first read the spec my reaction was basically "wait, that's it?" Because that's it. Once you can do writer.write(byte) from a webpage, the rest is just porting the protocol logic, which I'd already written once in Objective C. So this wasn't really a rewrite so much as a translation. Familiar territory. Anyone can write Javascript. Bootloader entry, block writes with length prefixed payloads, verify reads, retry on mismatch, exit, reboot. Same dance, new dance floor.
The Parts I Didn't Expect to Be Hard
If you'd told 25 year old me that someday I'd be debugging an AVR bootloader from Chrome DevTools, I wouldn't have believed you about the existence part. But also, I would have guessed wrong about what would actually be hard.
I figured the serial protocol or the encryption would be the rough parts. The encryption was fine. The protocol was fine. Web Crypto handled AES, DecompressionStream handled the ZIP, and an Intel HEX parser is one of those things that takes an hour to write and zero hours to debug because the format is small enough to fit in your head.
The actual rough parts were way less glamorous. Like, the device drops bytes if you send them too fast, so you need a one millisecond gap between every byte. Easy, right? Just await sleep(1) in the loop. Except setTimeout in browsers clamps to about four milliseconds after a few nested calls, so you wait four. Fine, busy loop on performance.now() instead. Now you're hitting one millisecond, but you've also got to make sure the previous byte actually left the WritableStream buffer before you start counting, otherwise the gap is between when you queued the byte and when you queued the next one, which is not at all the same thing as the gap on the wire. Solved that with await writer.ready, which is the Web Serial equivalent of tcdrain on POSIX.
None of that was in the spec I was originally porting from. None of it would have surprised the original Russian developer either, probably, because he was working on bare metal and didn't have a browser scheduler trying to be helpful. The whole thing is a good reminder that "porting" something is rarely just typing the same logic in a different language. The environment changes, and the environment has opinions.
What This Looks Like for You
If you're at home in front of your usual machine, the Mac and Windows clients aren't going anywhere. Use whatever you like. But if you're on a borrowed laptop, on the road with a Chromebook, sitting in a shop bay with a tablet, or you just don't feel like installing software to flash a firmware once a year, you've got a real option now.
Open zpu.zautomotive.com. Click connect. Pick the port. Done. The page knows your device once it reads the signature, pulls the right firmware automatically, decrypts it, writes it, verifies every byte, and reboots. If anything goes sideways there's a live log on the page that captures every command and response, which is also the easiest thing to copy paste into a support ticket.
Same Safety, Same Verify Pass
I want to be clear that "in the browser" doesn't mean we cut corners. The web client runs the exact same update flow as the desktop one. Enter bootloader, read signature, decrypt the package, erase, write the whole image, read it all back, byte for byte compare. Up to five retries per block, up to three full attempts. Nothing reboots until the verify pass actually passed. The desktop clients have been doing this for nine years. The web client does the same thing.
Privacy
Web Serial only connects to a port after you pick it from a browser dialog. A webpage cannot scan your USB devices, cannot connect silently, cannot grab a port you didn't grant. Firmware files are downloaded over HTTPS. The only network calls the page makes are fetching the update list and the firmware itself.
Closing
If you'd asked me in 2017 whether ZPU would run in a browser someday, I'd have laughed and gone back to writing Objective C. Nine years later, here we are. The Russian developer who wrote the original protocol in the early days probably never imagined a Chromebook, let alone a Chromebook flashing one of his bootloaders. I definitely didn't.
Anyway. Try it out at zpu.zautomotive.com. If you find a bug, the in page log is your friend, and so is our support email. Thanks for reading, and thanks for trusting us with your device, whether you've been using ZPU for nine years or nine minutes.