Three years of writing a browser engine, in 50 posts Gosub Browser project reached its 50th development blog post, marking three years of progress from a Rust novice to a modular browser engine with a multi-process proof-of-concept. The series, written by developer Jaytaph, documents milestones including a tokenizer passing all html5lib-tests, 99.89% of tree-construction tests passing, and a working DNS component. Fifty parts of Gosub Browser Progress 2026-08-01 ยท 1 week ago Fifty parts of Gosub Browser Progress Part 50 went up this week, which felt like a good moment to look back at the whole series. Three years, from "I don't know Rust and I don't know how browsers work" to a modular engine with a multi-process proof-of-concept. Here is the whole thing, one line per part. It reads a bit like a changelog of the project, and a bit like a changelog of me learning Rust. It started here: The one where he is writing a browser... https://codemusings.nl/@jaytaph/p/MQpHToAx8c1KXyU98Auip4 - the announcement, including the tiny issue that I didn't know Rust yet. Tokenizer pt1-pt9 pt1 https://codemusings.nl/@jaytaph/p/4PGS6LmaYWcqAtLbF2UmW9 - First days with Rust, and how much less explicit it is than Go. pt2 https://codemusings.nl/@jaytaph/p/FsX6jUZeDKjQHw6Z21tN3E - Fighting Rust over how to structure the thing. Unclear who won. pt3 https://codemusings.nl/@jaytaph/p/2QduMFxNzs5kZyjEAMWhPE - Named character references, finally done. They took far too long. pt4 https://codemusings.nl/@jaytaph/p/Vf343YLPXyHMtBdc3vxzGA - Character entities complete, so whole strings tokenize correctly. pt5 https://codemusings.nl/@jaytaph/p/B73pBMWqjLx2En1W4wGKgi - More data states filled in, plus generated testcases to check them. pt6 https://codemusings.nl/@jaytaph/p/Tsbmt1yFWdzS3QDNt9sCU7 - A test harness that runs the tokenizer against the html5 testsuite. pt7 https://codemusings.nl/@jaytaph/p/F6yS6qRu77VFGg4hs2qu26 - Over 5000 of 6000 token tests passing, with utf-8 as the sticking point. pt8 https://codemusings.nl/@jaytaph/p/AxuSNVFt8KMH6DpQkp7vKK - The tokenizer passes all html5lib-tests and lands in main. pt9 https://codemusings.nl/@jaytaph/p/LDMbsG2rMxHwctHUn2VwfJ - Tokenizer done, so on to tree construction. Tree construction and the borrow checker pt10-pt23 pt10 https://codemusings.nl/@jaytaph/p/J58UD8m7BzZgdprVXEu5Gk - Starting to get the hang of Rust. The borrow checker yells less. pt11 https://codemusings.nl/@jaytaph/p/YSwSALm1ksqT8ix7RipwYN - Document tree generation runs straight into ownership problems. pt12 https://codemusings.nl/@jaytaph/p/GGJamNEKiVzW8RxK5tSEHn - An arena holding all nodes, referenced by node id. pt13 https://codemusings.nl/@jaytaph/p/9rcsypRq2U59wvpekjE9Nf - Still wrestling the borrow checker, with an LLM as a second pair of eyes. pt14 https://codemusings.nl/@jaytaph/p/M5G27FEJEBDxoecAN9QzL8 - First attempts at the adoption agency algorithm. pt15 https://codemusings.nl/@jaytaph/p/WxUbXppbk6r2gzMGX9QSFd - Something snapped and the borrow checker finally made sense. pt16 https://codemusings.nl/@jaytaph/p/WFBnHUPqo4CYmbZeg8uLP8 - A blog post on why this project exists, which blew up on Hacker News. pt17 https://codemusings.nl/@jaytaph/p/C3QbhU3XeTA4CBVSLwdG2u - Still stuck on test 23 of the tree-construction tests. pt18 https://codemusings.nl/@jaytaph/p/CinQuVMDzW9i1DoTSnUFYX - Contributions start coming in, and my long-lived branch gets out of sync. pt19 https://codemusings.nl/@jaytaph/p/TUosNXnVo4X11PrTRKBYED - Test 23 defeated. The adoption agency works. pt20 https://codemusings.nl/@jaytaph/p/2EGZHQNSXjS8MHXpgRZqC5 - Filling in more tests, and struggling with Rust while doing it. pt21 https://codemusings.nl/@jaytaph/p/3v3FF4xoKR2xvMx9prpW5T - All 112 tests in test1.dat pass. Many files to go. pt22 https://codemusings.nl/@jaytaph/p/M7PiHzkWVx4m9qJKT25LMe - Parsing live documents from the actual web. pt23 https://codemusings.nl/@jaytaph/p/QbGZoeUveUSHNLoZeFbC2d - 99.89% of the tree-construction tests pass. Network stack and CSS pt24-pt30 pt24 https://codemusings.nl/@jaytaph/p/XMZpPyxQ4tSsSwWjDxvgxy - Designing the whole flow, and realising how much is still unwritten. pt25 https://codemusings.nl/@jaytaph/p/9KT5NRKrq5xyYA7gG8Le4r - The DNS component is finished. pt26 https://codemusings.nl/@jaytaph/p/243523MYmbn414zmN8tRBw - A week of yak shaving on the way to a http-to-rendertree pipeline. pt27 https://codemusings.nl/@jaytaph/p/YLB7aCTNMMj89ogDR2ezdq - A working css3 parser, at the cost of the previous one. pt28 https://codemusings.nl/@jaytaph/p/8npkEJtf2cx8bmFuGdcJwi - Porting csstree from JavaScript to Rust, which goes better than expected. pt29 https://codemusings.nl/@jaytaph/p/CnBMTLHSMZmMrTMhodXuEd - Real-life CSS parses, at the cost of ignoring the rest of the engine. pt30 https://codemusings.nl/@jaytaph/p/D1frA2X95PukV1A23z9e8T - Tying the html parser and the css3 parser together. First pixels pt31-pt43 pt31 https://codemusings.nl/@jaytaph/p/7awizheLS23YtFWS77mxKR - "dive", a text-based user agent built with ratatui. pt32 https://codemusings.nl/@jaytaph/p/KuxRowHetzgaDSmpiXrU9V - Tree-walking to get rudimentary text on screen. pt33 https://codemusings.nl/@jaytaph/p/RcsFyWGc5ZzV1zsv3peVk4 - Turning a CST into stylesheet structures usable for selectors. pt34 https://codemusings.nl/@jaytaph/p/LxaWzBWGhpQdpNdtEzSHjZ - Slower going, with the CSS system still far from finished. pt35 https://codemusings.nl/@jaytaph/p/FuYrqZju7JCiQagSNWQsga - Refactoring to break the cyclic dependency between html5 and css3. pt36 https://codemusings.nl/@jaytaph/p/MAxTbp6b2BWF1GzxQkL7bt - The CSS3 and HTML5 crates finally work together. pt37 https://codemusings.nl/@jaytaph/p/6oJ1dnx5LcshRSx5EfhAGm - Taking stock of the components, and thinking about event loops and async. pt38 https://codemusings.nl/@jaytaph/p/SUMjLniA2roBqUL8e4Yasq - The v2 engine proof-of-concept merges into main. pt39 https://codemusings.nl/@jaytaph/p/1xGnBMMqNGUw7T2RaksdiB - We've got a browser: a standalone useragent that loads tabs and renders them. pt40 https://codemusings.nl/@jaytaph/p/C8TqsM9mgKzfsKENN9XCas - Typography work, after the planning went sideways as usual. pt41 https://codemusings.nl/@jaytaph/p/FvfgpqDKC757EVP1SPMTdg - Font manager setbacks: the font libraries are not interchangeable. pt42 https://codemusings.nl/@jaytaph/p/A4e58uNvkzDHtsCmzjHEKd - The render pipeline split into separate phases, tiling and painting included. pt43 https://codemusings.nl/@jaytaph/p/9ibagj6xxE6sERk4vfeq6W - The rendering pipeline PoC turns html and css into actual pixels. Putting it together pt44-pt50 pt44 https://codemusings.nl/@jaytaph/p/XcjEE2cD4ic8sMKjxFyThu - Back at it after a quiet stretch, now with AI in the toolbox. pt45 https://codemusings.nl/@jaytaph/p/L5pxfiwMzMmNtNMAXSKTma - The big three merged: network stack, rendering pipeline and engine api. pt46 https://codemusings.nl/@jaytaph/p/3mzKJYonyAtac3vg2BfRvP - Modularity pays off, with several browsers built on the same engine. pt47 https://codemusings.nl/@jaytaph/p/UHWnwWdBtEtfonmjNKfW9Q - A new gosub.io, with a nautical theme. pt48 https://codemusings.nl/@jaytaph/p/C6KrES8iUP4VUUa81DEmNE - The engine back in a useful state, with ModuleConfig and RenderConfig. pt49 https://codemusings.nl/@jaytaph/p/TR39RSNTmGZP9FrKiNa2j4 - A refurbished Mac M1 to build and test on real hardware. pt50 https://codemusings.nl/@jaytaph/p/SyyydtLwLL4Z9wZFQ525jb - Cleanup, and a multi-process proof-of-concept. On to the next fifty. Nobody amused yet.