{
    "archive_path": "archive/1766149345.285067",
    "base_url": "blog.jsbarretto.com/post/software-is-joy",
    "basename": "software-is-joy",
    "bookmarked_date": "2025-12-19 13:02",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/blog.jsbarretto.com/post/software-is-joy",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=blog.jsbarretto.com",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": null
    },
    "domain": "blog.jsbarretto.com",
    "downloaded_at": "2025-12-19T13:02:36.518392+00:00",
    "downloaded_datestr": "2025-12-19 13:02",
    "extension": "",
    "hash": "1BB6Y7ZEG02ESYFWPXB4",
    "history": {
        "archive_org": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://web.archive.org/save/https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-19T13:04:34.302266+00:00",
                "index_texts": null,
                "output": "TimeoutExpired: Command '['/usr/bin/curl', '--silent', '--location', '--compressed', '--proxy', 'socks5://tor-socks-proxy:9150', '--head', '--max-time', '60', '--user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)', 'https://web.archive.org/save/https://blog.jsbarretto.com/post/software-is-joy']' timed out after 60 seconds",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:03:34.204329+00:00",
                "status": "failed"
            }
        ],
        "dom": [
            {
                "cmd": [
                    "/usr/bin/chromium-browser",
                    "--proxy-server=socks5://tor-socks-proxy:9150",
                    "--disable-features=DarkMode",
                    "--run-all-compositor-stages-before-draw",
                    "--hide-scrollbars",
                    "--autoplay-policy=no-user-gesture-required",
                    "--no-first-run",
                    "--use-fake-ui-for-media-stream",
                    "--use-fake-device-for-media-stream",
                    "--simulate-outdated-no-au='Tue, 31 Dec 2099 23:59:59 GMT'",
                    "--headless=new",
                    "--no-sandbox",
                    "--no-zygote",
                    "--disable-dev-shm-usage",
                    "--disable-software-rasterizer",
                    "--disable-sync",
                    "--window-size=1440,2000",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "--user-data-dir=/data/personas/Default/chrome_profile",
                    "--profile-directory=Default",
                    "--dump-dom",
                    "https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "131.0.6778",
                "end_ts": "2025-12-19T13:02:53.477725+00:00",
                "index_texts": null,
                "output": "output.html",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:02:46.085586+00:00",
                "status": "succeeded"
            }
        ],
        "favicon": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--max-time",
                    "60",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://www.google.com/s2/favicons?domain=blog.jsbarretto.com"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-19T13:02:40.151405+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:02:36.826724+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-19T13:02:40.717046+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:02:40.196654+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [
            {
                "cmd": [
                    "(internal) archivebox.extractors.htmltotext",
                    "./{singlefile,dom}.html"
                ],
                "cmd_version": "0.8.5rc51",
                "end_ts": "2025-12-19T13:03:22.619382+00:00",
                "index_texts": [
                    "(/static/img/profile.jpg)  Writing Toy Software Is A Joy (/) (A photograph of a man sat at the end of a small jetty, facing away from the camera)  (/) Home  (/rss.xml) RSS Feed   (https://www.github.com/zesterer) GitHub  (https://social.coop/@jsbarretto) Mastodon  (https://www.youtube.com/@jsbarretto) YouTube  (mailto:joshua@jsbarretto.com) E-Mail    Writing Toy Software Is A Joy Why you should write more toy programs 2025-06-15 | (5 comments) \u21a9\ufe0f 5 \u2502 (https://social.coop/users/jsbarretto/statuses/114688959803276435) (22 boosts) \ud83d\udd04 22 \u2502 (https://social.coop/users/jsbarretto/statuses/114688959803276435) (44 favourites) \u2b50 44   I am a huge fan of Richard Feyman\u2019s famous quote: \u201cWhat I cannot create, I do not understand\u201d  I think it\u2019s brilliant, and it remains true across many fields (if you\u2019re willing to be a little creative with the\ndefinition of \u2018create\u2019). It is to this principle that I believe I owe everything I\u2019m truly good at. Some will tell you\nto avoid reinventing the wheel, but they\u2019re wrong: you should build your own wheel, because it\u2019ll teach you more about\nhow they work than reading a thousand books on them ever will. In 2025, the beauty and craft of writing software is being eroded. AI is threatening to replace us (or, at least, the\nmost joyful aspects of our craft) and software development is being increasingly commodified, measured, packaged, and\nindustrialised. Software development needs more simple joy, and I\u2019ve found that creating toy programs is a great way to\nremember why I started working with computers again. Keep it simple Toy programs follow the 80:20 rule: 20% of the work, 80% of the functionality. The point is not to build\nproduction-worthy software (although it is true that some of the best production software began life as a toy).\nAggressively avoid over-engineering, restrict yourself to only whatever code is necessary to achieve your goal. Have\nevery code path panic/crash until you\u2019re forced to implement it to make progress. You might be surprised by just how\neasy it is to build toy versions of software you might previously have considered to be insummountably difficult to\ncreate. Other benefits I\u2019ve been consistently surprised by just how often some arcane nugget of knowledge I\u2019ve acquired when working on a toy\nproject has turned out to be immensely valuable in my day job, either by giving me a head-start on tracking down a\nproblem in a tool or library, or by recognising mistakes before they\u2019re made. Understanding the constraints that define the shape of software is vital for working with it, and there\u2019s no better way\nto gain insight into those constraints than by running into them head-first. You might even come up with some novel\nsolutions! The list Here is a list of toy programs I\u2019ve attempted over the past 15 years, rated by difficulty and time required. These\nratings are estimates and assume that you\u2019re already comfortable with at least one general-purpose programming language\nand that, like me, you tend to only have an hour or two per day free to write code. Also included are some suggested\nresources that I found useful. Regex engine (difficulty = 4/10, time = 5 days) A regex engine that can read a POSIX-style regex program and recognise strings that match it. Regex is simple yet\nshockingly expressive, and writing a competent regex engine will teach you everything you need to know about using the\nlanguage too. (https://en.wikipedia.org/wiki/Regular_expression#Syntax) Wikipedia: Regex   x86 OS kernel (difficulty = 7/10, time = 2 months) A multiboot-compatible OS kernel with a simple CLI, keyboard/mouse driver, ANSI escape sequence support, memory manager,\nscheduler, etc. Additional challenges include writing an in-memory filesystem, user mode and process isolation, loading\nELF executables, and supporting enough video hardware to render a GUI. (https://wiki.osdev.org/) OS Dev Wiki   (https://gitlab.com/zesterer/tupai) (Tupai)   GameBoy/NES emulator (difficulty = 6/10, time = 3 weeks) A crude emulator for the simplest GameBoy or NES games. The GB and the NES are classics, and both have relatively simple\ninstruction sets and peripheral hardware. Additional challenges include writing competent PPU (video) and PSG (audio)\nimplementations, along with dealing with some of the more exotic cartridge formats. (https://gbdev.io) GB Dev  (https://www.nesdev.org/wiki/Nesdev_Wiki) NES Dev Wiki   GameBoy Advance game (difficulty = 3/10, time = 2 weeks) A sprite-based game (top-down or side-on platform). The GBA is a beautiful little console to write code for and there\u2019s\nan active and dedicated development community for the console. I truly believe that the GBA is one of the last game\nconsoles that can be fully and completely understood by a single developer, right down to instruction timings. (https://www.coranac.com/tonc/text/toc.htm) Tonc  (https://problemkaputt.de/gbatek.htm) GBATEK   Physics engine (difficulty = 5/10, time = 1 week) A 2D rigid body physics engine that implements Newtonian physics with support for rectangles, circles, etc. On the\nsimplest end, just spheres that push away from one-another is quite simple to implement. Things start to get complex\nwhen you introduce more complex shapes, angular momentum, and the like. Additional challenges include making collision\nresolution fast and scaleable, having complex interactions move toward a steady state over time, soft-body interactions,\netc. Dynamic interpreter (difficulty = 4/10, time = 1-2 weeks) A tree-walking interpreter for a JavaScript-like language with basic flow control. There\u2019s an unbounded list of extra\nthings to add to this one, but being able to write programs in my own language still gives me child-like elation. It\nfeels like a sort of techno-genesis: once you\u2019ve got your own language, you can start building the universe within it. (https://craftinginterpreters.com/) Crafting Interpreters   (https://github.com/zesterer/forge) (Forge)   Compiler for a C-like (difficulty = 8/10, time = 3 months) A compiler for a simply-typed C-like programming language with support for at least one target archtecture. Extra\nchallenges include implementing some of the most common optimisations (inlining, const folding, loop-invariant code\nmotion, etc.) and designing an intermediate representation (IR) that\u2019s general enough to support multiple backends. Text editor (difficulty = 5/10, time = 2-4 weeks) This one has a lot of variability. At the blunt end, simply reading and writing a file can be done in a few lines of\nPython. But building something that\u2019s closer to a daily driver gets more complex. You could choose to implement the UI\nusing a toolkit like QT or GTK, but I personally favour an editor that works in the console. Properly handling unicode,\nsyntax highlighting, cursor movement, multi-buffer support, panes/windows, tabs, search/find functionality, LSP support,\netc. can all add between a week or a month to the project. But if you persist, you might join the elite company of those\ndevelopers who use an editor of their own creation. (https://github.com/zesterer/zte) (ZTE)   Async runtime (difficulty = 6/10, time = 1 week) There\u2019s a lot of language-specific variability as to what \u2018async\u2019 actually means. In Rust, at least, this means a\nlibrary that can ingest impl Future tasks and poll them concurrently until completion. Adding support for I/O waking\nmakes for a fun challenge. Hash map (difficulty = 4/10, time = 3-5 days) Hash maps (or sets/dictionaries, as a higher-level language might call them) are a programmer\u2019s bread & butter. And yet,\nsurprisingly few of us understand how they really work under the bonnet. There are a plethora of techniques to throw\ninto the mix too: closed or open addressing, tombstones, the robin hood rule, etc. You\u2019ll gain an appreciation for when\nand why they\u2019re fast, and also when you should just use a vector + linear search. (https://www.sebastiansylvan.com/post/robin-hood-hashing-should-be-your-default-hash-table-implementation/) Robin Hood Hashing should be your default Hash Table implementation   Rasteriser / texture-mapper (difficulty = 6/10, time = 2 weeks) Most of us have played with simple 3D graphics at some point, but how many of us truly understand how the graphics\npipeline works and, more to the point, how to fix it when it doesn\u2019t work? Writing your own software rasteriser will\ngive you that knowledge, along with a new-found appreciation for the beauty of vector maths and half-spaces that have\napplications across many other fields. Additional complexity involves properly implementing clipping, a Z-buffer, N-gon\nrasterisation, perspective-correct texture-mapping, Phong or Gouraud shading, shadow-mapping, etc. (https://www.scratchapixel.com/) Scratch-A-Pixel  (https://github.com/ssloy/tinyrenderer/wiki/Lesson-0:-getting-started) How OpenGL works: software rendering in 500 lines of code   (https://github.com/zesterer/euc) (euc)   SDF Rendering (difficulty = 5/10, time = 3 days) Signed Distance Fields are a beautifully simple way to render 3D spaces defined through mathematics, and are perfectly\nsuited to demoscene shaders. With relatively little work you can build yourself a cute little visualisation or some\nmoving shapes like the graphics demos of the 80s. You\u2019ll also gain an appreciation for shader languages and vector\nmaths. (https://iquilezles.org/articles/distfunctions/) Inigo Quilez\u2019s Site  (https://www.shadertoy.com/) ShaderToy   (https://www.shadertoy.com/view/ftXBWs) (Signed Distance Fields)   Voxel engine (difficulty = 5/10, time = 2 weeks) I doubt there are many reading this that haven\u2019t played Minecraft. It\u2019s surprisingly easy to build your own toy voxel\nengine cut from a similar cloth, especially if you\u2019ve got some knowledge of 3D graphics or game development already. The\nsimplicity of a voxel engine, combined with the near-limitless creativity that can be expressed with them, never ceases\nto fill me with joy. Additional complexity can be added by tackling textures, more complex procedural generation,\nfloodfill lighting, collisions, dynamic fluids, sending voxel data over the network, etc. (https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/) 0 FPS: Meshing in a Minecraft Game   Threaded Virtual Machine (difficulty = 6/10, time = 1 week) Writing interpreters is great fun. What\u2019s more fun? Faster interpreters . If you keep pushing interpreters as far as\nthey can go without doing architecture-specific codegen (like AOT or JIT), you\u2019ll eventually wind up (re)discovering threaded code (not to be confused with multi-threading, which is a very different beast). It\u2019s a beautiful way of\nweaving programs together out highly-optimised miniature programs, and a decent implementation can even give an AOT\ncompiler a run for its money in the performance department. (https://en.wikipedia.org/wiki/Threaded_code) Wikipedia: Threaded code  (https://muforth.dev/threaded-code/) muforth.dev: Threaded code   GUI Toolkit (difficulty = 6/10, time = 2-3 weeks) Most of us have probably cobbled together a GUI program using tkinter, GTK, QT, or WinForms. But why not try writing\nyour GUI toolkit? Additional complexity involves implementing a competent layout engine, good text shaping (inc.\nunicode support), accessibility support, and more. Fair warning: do not encourage people to use your tool unless it\u2019s battle-tested - the world has enough GUIs with little-to-no accessibility or localisation support. (https://www.youtube.com/watch?v=by9lQvpvMIc) YouTube: How Clay\u2019s UI Algorithm Works   (https://github.com/zesterer/gui) (GUI)   Orbital Mechanics Sim (difficulty = 6/10, time = 1 week) A simple simulation of Newtonian gravity can be cobbled together in a fairly short time. Infamously, gravitational\nsystems with more than two bodies cannot be solved analytically, so you\u2019ll have to get familiar with iterative integration methods. Additional complexity comes with implementing more precise and faster integration methods,\naccounting for relativistic effects, and writing a visualiser. If you\u2019ve got the maths right, you can even try plugging\nin real numbers from NASA to predict the next high tide or full moon. (https://en.wikipedia.org/wiki/Leapfrog_integration) Wikipedia: Leapfrog integration   Bitwise Challenge (difficulty = 3/10, time = 2-3 days) Here\u2019s one I came up with for myself, but I think it would make for a great game jam: write a game that only persists 64\nbits of state between subsequent frames. That\u2019s 64 bits for everything: the entire frame-for-frame game state should be\nreproducible using only 64 bits of data. It sounds simple, but it forces you to get incredibly creative with your game\nstate management. Details about the rules can be found on the GitHub page below. (https://github.com/zesterer/the-bitwise-challenge) The Bitwise Challenge   (https://github.com/zesterer/bitwise-examples) (Snake)   An ECS Framework (difficulty = 4/10, time = 1-2 weeks) For all those game devs out there: try building your own (https://en.wikipedia.org/wiki/Entity_component_system) ECS framework. It\u2019s not as hard as you might think (you might have accidentally done it already!). Extra points if you can\nbuild in safety and correctness features, as well as good integration with your programming language of choice\u2019s type\nsystem features. I built a custom ECS for my (https://www.youtube.com/watch?v=nS5rj80L-pk) Super Mario 64 on the GBA project due to the\nunique performance and memory constraints of the platform, and enjoyed it a lot.  CHIP-8 Emulator (difficulty = 3/10, time = 3-6 days) The (https://en.wikipedia.org/wiki/CHIP-8) CHIP-8 is a beautifully simple virtual machine from the 70s. You can write\na fully compliant emulator in a day or two, and there are an enormous plethora of fan-made games that run on it. (https://github.com/zesterer/emul8/raw/refs/heads/master/test/test.ch8) Here\u2019s a game I made for it. (https://en.wikipedia.org/wiki/CHIP-8) Wikipedia: CHIP-8   (https://github.com/zesterer/emul8) (Emul8)   Chess engine (difficulty = 5/10, time = 2-5 days) Writing a chess engine is great fun. You\u2019ll start off with every move it makes being illegal, but over time it\u2019ll get\nsmart and smarter. Experiencing a loss to your own chess engine really is a rite of passage, and it feels magical. (https://en.wikipedia.org/wiki/Minimax) Wikipedia: Minmax  (https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning) Wikipedia: Alpha-beta pruning   (Chess)  POSIX shell (difficulty = 4/10, time = 3-5 days) We interact with shells every day, and building one will teach you can incredible amount about POSIX - how it works, and\nhow it doesn\u2019t. A simple one can be built in a day, but compliance with an existing shell language will take time and\nteach you more than you ever wanted to know about its quirks. (https://brennan.io/2015/01/16/write-a-shell-in-c/) Write a shell in C   (https://github.com/zesterer/tosh) (Tosh)   A note on learning and LLMs Perhaps you\u2019re a user of LLMs. I get it, they\u2019re neat tools. They\u2019re useful for certain kinds of learning. But I might\nsuggest resisting the temptation to use them for projects like this. Knowledge is not supposed to be fed to you on a\nplate. If you want that sort of learning, read a book - the joy in building toy projects like this comes from an\nexploration of the unknown, without polluting one\u2019s mind with an existing solution. If you\u2019ve been using LLMs for a\nwhile, this cold-turkey approach might even be painful at first, but persist. There is no joy without pain. The runner\u2019s high doesn\u2019t come to those that take the bus.  (https://social.coop/users/jsbarretto/statuses/114688959803276435) Comments  Lunar \ud83d\udef8 \u267e | (https://dosgame.club/users/lunarloony/statuses/114689112769251941) 2025-06-15    (https://social.coop/@jsbarretto) @jsbarretto   Once regex \"clicked\", I found myself making excuses to use it   vekkq | (https://social.vivaldi.net/users/vekkq/statuses/114907703772440265) 2025-07-24    (https://social.coop/@jsbarretto) @jsbarretto   arguably its easier to write toys today, when so much power is at your finger tips that even the worst performing solution becomes viable.   Joshua Barretto | (https://social.coop/users/jsbarretto/statuses/114908243078710217) 2025-07-24    (https://social.vivaldi.net/@vekkq) @vekkq   If you're building something yourself, you'll struggle to build something slow, even if you try. Poorly performing software is more often caused by devs using over-powered pre-built frameworks and toolkits built for heavy duty use for software that simply doesn't need it.   vekkq | (https://social.vivaldi.net/users/vekkq/statuses/114908584207533505) 2025-07-24    (https://social.coop/@jsbarretto) @jsbarretto   yes, its often the culprit.  it is also possible to use simpler functions that solve an issue in a less efficient way.  E.g. if you implement the longest common subsequence. Intersect all subsequences and compare them instead of writing an optimized algorithm.   Joshua Barretto | (https://social.coop/users/jsbarretto/statuses/114908836697250015) 2025-07-24    (https://social.vivaldi.net/@vekkq) @vekkq   That can be true in rare cases, yes - but the point of the exercise is to allow a simpler codebase to guide you more easily toward the source of slowdowns and think more holistically about the program. I don't think modern hardware makes toy software any more or less possible. Toy software was pretty much *the default* in days gone by, and the performance of computer systems mostly did not suffer as a result.     (https://content.jsbarretto.com/void) Enter the tarpit \ud83e\udd16  If you notice accessibility issues with this site, please (mailto:joshua@jsbarretto.com) let me know !  \u00a9 Joshua Barretto        "
                ],
                "output": "htmltotext.txt",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:03:22.538340+00:00",
                "status": "succeeded"
            }
        ],
        "media": [
            {
                "cmd": [
                    "/usr/local/bin/yt-dlp",
                    "--restrict-filenames",
                    "--trim-filenames",
                    "128",
                    "--write-description",
                    "--write-info-json",
                    "--write-annotations",
                    "--write-thumbnail",
                    "--no-call-home",
                    "--write-sub",
                    "--write-auto-subs",
                    "--convert-subs=srt",
                    "--yes-playlist",
                    "--continue",
                    "--no-abort-on-error",
                    "--ignore-errors",
                    "--geo-bypass",
                    "--add-metadata",
                    "--format=(bv*+ba/b)[filesize<=750m][filesize_approx<=?750m]/(bv*+ba/b)",
                    "--skip-download",
                    "--cache-dir=/data/yt-dlp-cache/",
                    "--cookies=/data/yt-dlp-cache/cookies.txt",
                    "--proxy=socks5://tor-socks-proxy:9150",
                    "--no-playlist",
                    "https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "2024.10.7",
                "end_ts": "2025-12-19T13:03:34.081984+00:00",
                "index_texts": [],
                "output": "ArchiveError: Failed to save media",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:03:24.782559+00:00",
                "status": "failed"
            }
        ],
        "mercury": [
            {
                "cmd": [
                    "/home/archivebox/.npm/bin/postlight-parser",
                    "https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "2.2.3",
                "end_ts": "2025-12-19T13:03:22.375983+00:00",
                "index_texts": null,
                "output": "mercury/",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:03:18.061141+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [],
        "readability": [
            {
                "cmd": [
                    "/home/archivebox/.npm/bin/readability-extractor",
                    "/tmp/tmpakg0jzp1",
                    "https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2025-12-19T13:02:58.113629+00:00",
                "index_texts": [
                    "Why you should write more toy programs\n                \n                \n            \n                I am a huge fan of Richard Feyman\u2019s famous quote:\n\n\u201cWhat I cannot create, I do not understand\u201d\n\nI think it\u2019s brilliant, and it remains true across many fields (if you\u2019re willing to be a little creative with the\ndefinition of \u2018create\u2019). It is to this principle that I believe I owe everything I\u2019m truly good at. Some will tell you\nto avoid reinventing the wheel, but they\u2019re wrong: you should build your own wheel, because it\u2019ll teach you more about\nhow they work than reading a thousand books on them ever will.\nIn 2025, the beauty and craft of writing software is being eroded. AI is threatening to replace us (or, at least, the\nmost joyful aspects of our craft) and software development is being increasingly commodified, measured, packaged, and\nindustrialised. Software development needs more simple joy, and I\u2019ve found that creating toy programs is a great way to\nremember why I started working with computers again.\nKeep it simple\nToy programs follow the 80:20 rule: 20% of the work, 80% of the functionality. The point is not to build\nproduction-worthy software (although it is true that some of the best production software began life as a toy).\nAggressively avoid over-engineering, restrict yourself to only whatever code is necessary to achieve your goal. Have\nevery code path panic/crash until you\u2019re forced to implement it to make progress. You might be surprised by just how\neasy it is to build toy versions of software you might previously have considered to be insummountably difficult to\ncreate.\nOther benefits\nI\u2019ve been consistently surprised by just how often some arcane nugget of knowledge I\u2019ve acquired when working on a toy\nproject has turned out to be immensely valuable in my day job, either by giving me a head-start on tracking down a\nproblem in a tool or library, or by recognising mistakes before they\u2019re made.\nUnderstanding the constraints that define the shape of software is vital for working with it, and there\u2019s no better way\nto gain insight into those constraints than by running into them head-first. You might even come up with some novel\nsolutions!\nThe list\nHere is a list of toy programs I\u2019ve attempted over the past 15 years, rated by difficulty and time required. These\nratings are estimates and assume that you\u2019re already comfortable with at least one general-purpose programming language\nand that, like me, you tend to only have an hour or two per day free to write code. Also included are some suggested\nresources that I found useful.\nRegex engine (difficulty = 4/10, time = 5 days)\nA regex engine that can read a POSIX-style regex program and recognise strings that match it. Regex is simple yet\nshockingly expressive, and writing a competent regex engine will teach you everything you need to know about using the\nlanguage too.\n\nWikipedia: Regex\n\nx86 OS kernel (difficulty = 7/10, time = 2 months)\nA multiboot-compatible OS kernel with a simple CLI, keyboard/mouse driver, ANSI escape sequence support, memory manager,\nscheduler, etc. Additional challenges include writing an in-memory filesystem, user mode and process isolation, loading\nELF executables, and supporting enough video hardware to render a GUI.\n\nOS Dev Wiki\n\n\nGameBoy/NES emulator (difficulty = 6/10, time = 3 weeks)\nA crude emulator for the simplest GameBoy or NES games. The GB and the NES are classics, and both have relatively simple\ninstruction sets and peripheral hardware. Additional challenges include writing competent PPU (video) and PSG (audio)\nimplementations, along with dealing with some of the more exotic cartridge formats.\n\nGB Dev\nNES Dev Wiki\n\nGameBoy Advance game (difficulty = 3/10, time = 2 weeks)\nA sprite-based game (top-down or side-on platform). The GBA is a beautiful little console to write code for and there\u2019s\nan active and dedicated development community for the console. I truly believe that the GBA is one of the last game\nconsoles that can be fully and completely understood by a single developer, right down to instruction timings.\n\nTonc\nGBATEK\n\nPhysics engine (difficulty = 5/10, time = 1 week)\nA 2D rigid body physics engine that implements Newtonian physics with support for rectangles, circles, etc. On the\nsimplest end, just spheres that push away from one-another is quite simple to implement. Things start to get complex\nwhen you introduce more complex shapes, angular momentum, and the like. Additional challenges include making collision\nresolution fast and scaleable, having complex interactions move toward a steady state over time, soft-body interactions,\netc.\nDynamic interpreter (difficulty = 4/10, time = 1-2 weeks)\nA tree-walking interpreter for a JavaScript-like language with basic flow control. There\u2019s an unbounded list of extra\nthings to add to this one, but being able to write programs in my own language still gives me child-like elation. It\nfeels like a sort of techno-genesis: once you\u2019ve got your own language, you can start building the universe within it.\n\nCrafting Interpreters\n\n\nCompiler for a C-like (difficulty = 8/10, time = 3 months)\nA compiler for a simply-typed C-like programming language with support for at least one target archtecture. Extra\nchallenges include implementing some of the most common optimisations (inlining, const folding, loop-invariant code\nmotion, etc.) and designing an intermediate representation (IR) that\u2019s general enough to support multiple backends.\nText editor (difficulty = 5/10, time = 2-4 weeks)\nThis one has a lot of variability. At the blunt end, simply reading and writing a file can be done in a few lines of\nPython. But building something that\u2019s closer to a daily driver gets more complex. You could choose to implement the UI\nusing a toolkit like QT or GTK, but I personally favour an editor that works in the console. Properly handling unicode,\nsyntax highlighting, cursor movement, multi-buffer support, panes/windows, tabs, search/find functionality, LSP support,\netc. can all add between a week or a month to the project. But if you persist, you might join the elite company of those\ndevelopers who use an editor of their own creation.\n\nAsync runtime (difficulty = 6/10, time = 1 week)\nThere\u2019s a lot of language-specific variability as to what \u2018async\u2019 actually means. In Rust, at least, this means a\nlibrary that can ingest impl Future tasks and poll them concurrently until completion. Adding support for I/O waking\nmakes for a fun challenge.\nHash map (difficulty = 4/10, time = 3-5 days)\nHash maps (or sets/dictionaries, as a higher-level language might call them) are a programmer\u2019s bread & butter. And yet,\nsurprisingly few of us understand how they really work under the bonnet. There are a plethora of techniques to throw\ninto the mix too: closed or open addressing, tombstones, the robin hood rule, etc. You\u2019ll gain an appreciation for when\nand why they\u2019re fast, and also when you should just use a vector + linear search.\n\nRobin Hood Hashing should be your default Hash Table implementation\n\nRasteriser / texture-mapper (difficulty = 6/10, time = 2 weeks)\nMost of us have played with simple 3D graphics at some point, but how many of us truly understand how the graphics\npipeline works and, more to the point, how to fix it when it doesn\u2019t work? Writing your own software rasteriser will\ngive you that knowledge, along with a new-found appreciation for the beauty of vector maths and half-spaces that have\napplications across many other fields. Additional complexity involves properly implementing clipping, a Z-buffer, N-gon\nrasterisation, perspective-correct texture-mapping, Phong or Gouraud shading, shadow-mapping, etc.\n\nScratch-A-Pixel\nHow OpenGL works: software rendering in 500 lines of code\n\n\nSDF Rendering (difficulty = 5/10, time = 3 days)\nSigned Distance Fields are a beautifully simple way to render 3D spaces defined through mathematics, and are perfectly\nsuited to demoscene shaders. With relatively little work you can build yourself a cute little visualisation or some\nmoving shapes like the graphics demos of the 80s. You\u2019ll also gain an appreciation for shader languages and vector\nmaths.\n\nInigo Quilez\u2019s Site\nShaderToy\n\n\nVoxel engine (difficulty = 5/10, time = 2 weeks)\nI doubt there are many reading this that haven\u2019t played Minecraft. It\u2019s surprisingly easy to build your own toy voxel\nengine cut from a similar cloth, especially if you\u2019ve got some knowledge of 3D graphics or game development already. The\nsimplicity of a voxel engine, combined with the near-limitless creativity that can be expressed with them, never ceases\nto fill me with joy. Additional complexity can be added by tackling textures, more complex procedural generation,\nfloodfill lighting, collisions, dynamic fluids, sending voxel data over the network, etc.\n\n0 FPS: Meshing in a Minecraft Game\n\nThreaded Virtual Machine (difficulty = 6/10, time = 1 week)\nWriting interpreters is great fun. What\u2019s more fun? Faster interpreters. If you keep pushing interpreters as far as\nthey can go without doing architecture-specific codegen (like AOT or JIT), you\u2019ll eventually wind up (re)discovering\nthreaded code (not to be confused with multi-threading, which is a very different beast). It\u2019s a beautiful way of\nweaving programs together out highly-optimised miniature programs, and a decent implementation can even give an AOT\ncompiler a run for its money in the performance department.\n\nWikipedia: Threaded code\nmuforth.dev: Threaded code\n\nGUI Toolkit (difficulty = 6/10, time = 2-3 weeks)\nMost of us have probably cobbled together a GUI program using tkinter, GTK, QT, or WinForms. But why not try writing\nyour GUI toolkit? Additional complexity involves implementing a competent layout engine, good text shaping (inc.\nunicode support), accessibility support, and more. Fair warning: do not encourage people to use your tool unless it\u2019s\nbattle-tested - the world has enough GUIs with little-to-no accessibility or localisation support.\n\nYouTube: How Clay\u2019s UI Algorithm Works\n\n\nOrbital Mechanics Sim (difficulty = 6/10, time = 1 week)\nA simple simulation of Newtonian gravity can be cobbled together in a fairly short time. Infamously, gravitational\nsystems with more than two bodies cannot be solved analytically, so you\u2019ll have to get familiar with iterative\nintegration methods. Additional complexity comes with implementing more precise and faster integration methods,\naccounting for relativistic effects, and writing a visualiser. If you\u2019ve got the maths right, you can even try plugging\nin real numbers from NASA to predict the next high tide or full moon.\n\nWikipedia: Leapfrog integration\n\nBitwise Challenge (difficulty = 3/10, time = 2-3 days)\nHere\u2019s one I came up with for myself, but I think it would make for a great game jam: write a game that only persists 64\nbits of state between subsequent frames. That\u2019s 64 bits for everything: the entire frame-for-frame game state should be\nreproducible using only 64 bits of data. It sounds simple, but it forces you to get incredibly creative with your game\nstate management. Details about the rules can be found on the GitHub page below.\n\nThe Bitwise Challenge\n\n\nAn ECS Framework (difficulty = 4/10, time = 1-2 weeks)\nFor all those game devs out there: try building your own ECS\nframework. It\u2019s not as hard as you might think (you might have accidentally done it already!). Extra points if you can\nbuild in safety and correctness features, as well as good integration with your programming language of choice\u2019s type\nsystem features.\nI built a custom ECS for my Super Mario 64 on the GBA project due to the\nunique performance and memory constraints of the platform, and enjoyed it a lot.\n\nCHIP-8 Emulator (difficulty = 3/10, time = 3-6 days)\nThe CHIP-8 is a beautifully simple virtual machine from the 70s. You can write\na fully compliant emulator in a day or two, and there are an enormous plethora of fan-made games that run on it.\nHere\u2019s a game I made for it.\n\nWikipedia: CHIP-8\n\n\nChess engine (difficulty = 5/10, time = 2-5 days)\nWriting a chess engine is great fun. You\u2019ll start off with every move it makes being illegal, but over time it\u2019ll get\nsmart and smarter. Experiencing a loss to your own chess engine really is a rite of passage, and it feels magical.\n\nWikipedia: Minmax\nWikipedia: Alpha-beta pruning\n\n\nPOSIX shell (difficulty = 4/10, time = 3-5 days)\nWe interact with shells every day, and building one will teach you can incredible amount about POSIX - how it works, and\nhow it doesn\u2019t. A simple one can be built in a day, but compliance with an existing shell language will take time and\nteach you more than you ever wanted to know about its quirks.\n\nWrite a shell in C\n\n\nA note on learning and LLMs\nPerhaps you\u2019re a user of LLMs. I get it, they\u2019re neat tools. They\u2019re useful for certain kinds of learning. But I might\nsuggest resisting the temptation to use them for projects like this. Knowledge is not supposed to be fed to you on a\nplate. If you want that sort of learning, read a book - the joy in building toy projects like this comes from an\nexploration of the unknown, without polluting one\u2019s mind with an existing solution. If you\u2019ve been using LLMs for a\nwhile, this cold-turkey approach might even be painful at first, but persist. There is no joy without pain.\nThe runner\u2019s high doesn\u2019t come to those that take the bus."
                ],
                "output": "readability/",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:02:54.758204+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [],
        "singlefile": [],
        "title": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--proxy",
                    "socks5://tor-socks-proxy:9150",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://blog.jsbarretto.com/post/software-is-joy"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2025-12-19T13:02:53.591067+00:00",
                "index_texts": null,
                "output": "Writing Toy Software Is A Joy",
                "pwd": "/data/archive/1766149345.285067",
                "schema": "ArchiveResult",
                "start_ts": "2025-12-19T13:02:53.549173+00:00",
                "status": "succeeded"
            }
        ],
        "wget": []
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": "TimeoutExpired: Command '['/usr/bin/curl', '--silent', '--location', '--compressed', '--proxy', 'socks5://tor-socks-proxy:9150', '--head', '--max-time', '60', '--user-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)', 'https://web.archive.org/save/https://blog.jsbarretto.com/post/software-is-joy']' timed out after 60 seconds",
        "dom": "output.html",
        "favicon": "favicon.ico",
        "git": null,
        "media": "ArchiveError: Failed to save media",
        "pdf": null,
        "screenshot": null,
        "singlefile": null,
        "title": "Writing Toy Software Is A Joy",
        "warc": null,
        "wget": null
    },
    "link_dir": "/data/archive/1766149345.285067",
    "newest_archive_date": "2025-12-19T13:03:34.204329+00:00",
    "num_failures": 2,
    "num_outputs": 7,
    "oldest_archive_date": "2025-12-19T13:02:36.826724+00:00",
    "path": "/post/software-is-joy",
    "schema": "Link",
    "scheme": "https",
    "snapshot_abid": "snp_01KCVB8M1YC8EA505D01EX4A1E",
    "snapshot_id": "f754f459-5f56-475c-aa46-04365dd2282e",
    "sources": [
        "/data/sources/1766149343-import.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1766149345.285067",
    "title": "Writing Toy Software Is A Joy",
    "url": "https://blog.jsbarretto.com/post/software-is-joy"
}