<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Pablo Murad</title>
    <subtitle>Building things and leaving them scattered around to keep the internet free.</subtitle>
    <link href="https://pablomurad.com/atom-full/" rel="self" type="application/atom+xml"/>
    <link href="https://websubhub.com/hub" rel="hub"/>
    <link href="https://pablomurad.com/"/>
    <id>https://pablomurad.com/</id>
    <updated>2026-11-12T07:47:21-03:00</updated>
    <generator>pablawn-v1</generator>
    <entry>
        <title>Neat Finds #06: Gopher Holes, Beige Boxes and Unix Since 1970</title>
        <link href="https://pablomurad.com/neat-finds-06-gopher-holes-beige-boxes-and-unix-since-1970/"/>
        <id>https://pablomurad.com/neat-finds-06-gopher-holes-beige-boxes-and-unix-since-1970/</id>
        <published>2026-11-12T07:04:29-03:00</published>
        <updated>2026-11-12T07:04:29-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="neat-finds"/>
        <summary>Issue six: browsers and servers for Gemini, Gopher and Finger, an ephemeral forum, a beige DOS PC drawn from scratch, Serial Experiments Lain decompiled, PSP games in the browser, Zork&apos;s source and the whole history of Unix in one Git repo.</summary>
        <content type="html"><![CDATA[<p>Issue six comes in two halves. The first is the small internet, seen from both ends: browsers for Gemini, Gopher, Finger and friends, a few servers to answer them, and a couple of odd forums. The second half goes back in time, with a beige DOS box drawn from scratch, a PlayStation game rebuilt from its own source, PSP games in a browser tab, and some code that has been sitting in archives for decades. As usual, no AI projects.</p><p>Same deal as always: I haven't tested everything here, and the last lines of each entry are what I'd be tempted to build, not promises.</p><h3 id="01-tocaia">01. Tocaia</h3><p><a href="https://github.com/manipuladordedados/tocaia">github.com/manipuladordedados/tocaia</a></p><p>A small Gopher client written in C89 with POSIX in mind. It has a text interface for walking around Gopherspace, with menus, text files, searches and back and forward history, and it needs nothing more than a C compiler: clone it, run <code>make install</code> and you're in. The name is Portuguese for an ambush. A client this light would be an easy thing to offer every user on runv.club.</p><h3 id="02-telescope">02. Telescope</h3><p><a href="https://github.com/telescope-browser/telescope">github.com/telescope-browser/telescope</a></p><p>A browser for the small internet, inspired by Emacs and w3m, that speaks Gemini, Gopher and Finger. It has the expected things (tabs, bookmarks, history, client certificates) and a privilege-separated design, and the code follows OpenBSD's style guide. The README lists fun as the first goal and says the project is entirely handwritten, with contributions generated by LLMs not accepted. For certificates it goes beyond plain trust on first use, with three levels: untrusted, trusted and verified out of band. Out of the box, the external editor is <code>ed</code>.</p><h3 id="03-rogallo">03. Rogallo</h3><p><a href="https://github.com/davep/rogallo">github.com/davep/rogallo</a></p><p>A terminal client in Python for six protocols: Gemini, Titan, Spartan, Gopher, Nex and Finger. Bookmarks and history both have search, there's a command palette, a source viewer and mouse support, and it renders ANSI escape sequences inside pages. On the Gemini side it handles input prompts, with an option to type them in your own editor, and it can create client certificates for you and keep one per capsule. Install it with <code>uv tool install rogallo</code> or <code>pipx install rogallo</code>. Pointing it at Soledade over Nex would be my first test.</p><h3 id="04-alhena">04. Alhena</h3><p><a href="https://github.com/mochaman/alhena">github.com/mochaman/alhena</a></p><p>For people who'd rather not live in a terminal: a desktop browser for Gemini, Gopher, Spartan and Nex, built on Java 21, with dozens of themes, color emoji and any system font for text. The feature list is long. Inline images, audio and video through VLC, Titan uploads, split view, gemlog and Atom subscriptions, a Gemtext-aware editor, an HTML-to-Gemtext converter, and backups and remote sync for your settings and certificates. There are installers on the <a href="https://metaloupe.com/alhena/alhena.html">project page</a>. It's the one I'd hand to a friend who wants to see what Gemini looks like.</p><h3 id="05-smolserve">05. smolserve</h3><p><a href="https://github.com/davep/smolserve">github.com/davep/smolserve</a></p><p>From the same author as Rogallo, a lightweight server for Gemini, Gopher, Finger, Spartan and Nex at once, meant for local testing and documentation. Run it with no setup and it listens on 127.0.0.1 with Gemini on 1965, Gopher on 7070, Finger on 7979, Spartan on 3000 and Nex on 1900, creating sample content folders and self-signed certificates if they don't exist. The clever part is <code>exec</code> mode: <code>smolserve exec -- pytest</code> keeps the servers up only while that command runs, then shuts them down and returns its exit code. Not to be confused with SmolServ, without the e, as the README points out. It would be a good place to preview a capsule before it goes live.</p><h3 id="06-fleur">06. fleur</h3><p><a href="https://github.com/theobori/fleur">github.com/theobori/fleur</a></p><p>A framework in Go for building Gopher applications, plus a CLI that serves files. You map paths to functions with regular expressions, so a request for <code>/dice</code> can answer with a fresh gophermap holding a dice roll, and extensions can add new item types to the gophermap format itself. Gopher over TLS works on the same port as plain Gopher. The CLI can also serve personal Gopherspaces, turning <code>/~username/</code> into that user's <code>public_gopher</code> folder, which is exactly how a pubnix likes it. The name is French for flower, and there's a NixOS module. runv.club's users could each get a gopher hole this way.</p><h3 id="07-gigi">07. Gigi</h3><p><a href="https://github.com/Fuwn/gigi">github.com/Fuwn/gigi</a></p><p>"An honest Finger protocol server", with few features, "the way a Finger server should be." It answers requests from static files, or dynamically by running whatever executable you put at <code>.gigi/do</code>, which is off by default because, as the README says, running code on request is a real risk. There's a live instance you can poke at with <code>finger fuwn.me</code>, or with <code>echo | nc fuwn.me 79</code> if you don't have a client. It runs with Go, Nix or Docker. After the <a href="https://pablomurad.com/practical-guide-to-webfinger/">WebFinger guide</a>, the original finger is the obvious next toy.</p><h3 id="08-miniflux-gemini">08. miniflux-gemini</h3><p><a href="https://github.com/cljoly/miniflux-gemini">github.com/cljoly/miniflux-gemini</a></p><p>Exposes a Miniflux instance over Gemini, so you can read your feeds from a Gemini browser. Login uses client certificates: you add a row that ties your certificate's fingerprint to your Miniflux address and an API token, and the server knows who you are from then on. The README marks it as a work in progress with thin documentation, so expect to read some code. Feeds read from Telescope on an old laptop sounds like a nice quiet morning.</p><h3 id="09-neon-modem-overdrive">09. Neon Modem Overdrive</h3><p><a href="https://github.com/mrusme/neonmodem">github.com/mrusme/neonmodem</a></p><p>A BBS-style command-line client that puts Discourse, Lemmy, Lobsters, Hacker News and Hyperuplink behind one text interface. You can list forums, posts and replies on all of them, read without an account, and post or reply once you log in. It's written in Go with Bubble Tea. On the name, the README says it plainly: "And yes, you heard that right, I really did call it Neon Modem Overdrive." Packages exist for Arch, through the AUR, and NetBSD.</p><h3 id="10-tmpbbs">10. tmpbbs</h3><p><a href="https://github.com/mmb/tmpbbs">github.com/mmb/tmpbbs</a></p><p>An ephemeral, anonymous forum. Posts live in memory and disappear when the process stops, and the whole thing is one static binary that needs no internet and no disk, small enough for an old wireless router. Durability comes from linking instances: posts can replicate in one direction or both, so you can build a mesh, a tree, backups or read-only copies. Every instance serves its own binary at <code>/self</code>, so anyone who visits can start another one. There are tripcodes in the <code>name#secret</code> style and post IDs you can read aloud. A forum that only exists for the length of an event is a lovely idea.</p><h3 id="11-term-everything">11. term.everything</h3><p><a href="https://github.com/mmulet/term.everything">github.com/mmulet/term.everything</a></p><p>Runs GUI apps inside your terminal, even over SSH. It's a Wayland compositor written from scratch in Go, with a little C, that draws windows to the terminal instead of to a monitor. Quality depends on how many rows and columns you have, so shrinking the font makes the picture sharper, and in terminals that show images, like kitty or iTerm2, windows can render at full resolution. With a small hack it runs Doom. The roadmap has three steps, and the project sits on the first: "Term some things". The <a href="https://github.com/mmulet/term.everything/blob/main/resources/HowIDidIt.md">HowIDidIt.md</a> file is a good read about Wayland on its own.</p><h3 id="12-cool-retro-term">12. cool-retro-term</h3><p><a href="https://github.com/Swordfish90/cool-retro-term">github.com/Swordfish90/cool-retro-term</a></p><p>A terminal emulator that looks like an old cathode-ray screen, with glow, curvature and scanlines, and profiles like amber, green and IBM DOS. It's built on the QML port of Konsole's terminal widget, needs Qt6 and runs on Linux and macOS. Most distributions already package it, and there are AppImage and dmg builds on the releases page. It's been around for years, and opening it before an <code>ssh runv.club</code> session would set the mood.</p><h3 id="13-dos-ex-machina">13. DOS ex Machina</h3><p><a href="https://github.com/pedrocatalao/dos-ex-machina">github.com/pedrocatalao/dos-ex-machina</a></p><p>An early 90s beige-box PC on your screen, case and CRT included, that boots a real DOS. It powers on, runs its POST and hands over to DOSBox Pure at the prompt. Nothing is a photograph: the machine is drawn procedurally at your display's resolution, and the tube has phosphor persistence, curvature, scanlines, an aperture-grille mask and light from the picture spilling onto the plastic. The sound gets the same care, with the relay and degauss thump at power on, fans, the disk spinning up, the floppy when a program loads, and a mechanical keyboard cut from recordings of real typing. C: is a folder on your computer, <code>SETUP</code> lets you pick processor, memory and even a 3dfx card, and <code>CATALOG</code> downloads freeware and shareware from where the authors published it. There are no downloads yet, so you build it from source. I'd fill that C: drive the first evening.</p><h3 id="14-launch">14. Launch!</h3><p><a href="https://github.com/therenegar/launch">github.com/therenegar/launch</a></p><p>A command launcher for any DOS from 3.3 up, on a 286 or better with EGA or VGA. It works with MS-DOS, PC DOS, DR-DOS and FreeDOS, on real hardware or in DOSBox and 86Box. Instead of replacing the prompt with a graphical shell, it keeps you in text mode and pops a menu over whatever is on screen when you press <code>Ctrl+Alt+\</code>, then runs your choice by typing it for you through keyboard macros. Around that come accessories (calendar, calculator, journal), text-mode games like Snake and Solitaire, screensavers including a starry skyline and a fake disk defrag, and integration with Windows 3.x. It installs from a floppy image, or from a real floppy if you have one. It would look right at home inside DOS ex Machina.</p><h3 id="15-serial-experiments-lain-decompiled">15. Serial Experiments Lain, decompiled</h3><p><a href="https://github.com/Y0oshi/lain-psx-decompiled">github.com/Y0oshi/lain-psx-decompiled</a></p><p>A decompilation of Serial Experiments Lain for the PlayStation, the 1998 Pioneer LDC game. All 321 game functions are now C that compiles with the original PsyQ toolchain into an executable byte-identical to the retail one. On top of that sits a native client for Windows, macOS and Linux that runs the game with no emulator, and it plays the whole thing, both discs and all four endings. No game data is included: you dump your own discs and the launcher checks them. It also adds mods, Lua scripting and a tab of content found on the discs that the game never uses. Issue four had lainTSX, a remake of the game's interface in the browser; this one goes all the way down to the original code.</p><h3 id="16-psp-web-recomp">16. PSP Web Recomp</h3><p><a href="https://github.com/snuri00/psp-web-recomp">github.com/snuri00/psp-web-recomp</a></p><p>PSP games in a browser tab. The game's MIPS code is translated ahead of time into C++, compiled to WebAssembly and linked against a small reimplementation of the PSP's operating system and graphics chip, drawn with WebGL2. The first game brought up is God of War: Chains of Olympus, which the README says runs at 60 frames per second in the scenes measured so far, at up to four times the PSP's resolution, and on phones with touch controls; Ghost of Sparta followed. You bring a disc image of a game you own and the scripts turn it into a page on your own machine, about 2 GB of disk per game. The author is honest that both games share one engine, so a game from another studio will probably stop at a system call nobody has written yet. Reading the "How it works" section is worth it even without a PSP disc in the house.</p><h3 id="17-fuzix">17. FUZIX</h3><p><a href="https://github.com/EtchedPixels/FUZIX">github.com/EtchedPixels/FUZIX</a></p><p>"Because Small Is Beautiful." A Unix-like operating system for small and old processors, made by fusing several forks of UZI and growing them from Version 7 toward System III and System V, with some POSIX on top. The kernel boots to userspace on a long list of chips: Z80, 6502, 6809, 68000, 8080, 8085, ARM32, even the ESP8266. The README calls it "an art project in computing", says the best support is code or docs, and if you'd rather spend money, suggests buying a homeless person a pizza or a coat. Some prebuilt filesystems are on <a href="https://www.fuzix.org">fuzix.org</a>. It's a good excuse to get a Z80 board.</p><h3 id="18-unix-history-repository">18. Unix History Repository</h3><p><a href="https://github.com/dspinellis/unix-history-repo">github.com/dspinellis/unix-history-repo</a></p><p>The whole history of Unix as one Git repository, from 1970, when it was a 2.5 thousand line kernel and 48 commands, to 2025, when it's a 41 million line system. It was built by stitching together 22 snapshots from Bell Labs, Berkeley, the 386BSD team and modern FreeBSD, and it holds about 850 thousand commits and some 2,700 identified contributors, the early ones found through primary research. The repository is regenerated from scratch, so contributions go to its sister project, unix-history-make. Clone it, check out the PDP-7 snapshot and read where all of this started.</p><h3 id="19-zork-i">19. Zork I</h3><p><a href="https://github.com/historicalsource/zork1">github.com/historicalsource/zork1</a></p><p>The source code of Infocom's Zork I, the 1980 game by Marc Blank, Dave Lebling, Bruce Daniels and Tim Anderson. It's written in ZIL, the Zork Implementation Language, built on MDL, a Lisp dialect from MIT. The files are a snapshot of Infocom's development directory at the time the company shut down, including some that were discarded along the way. There's no official compiler left, but the community-made ZILF has been shown to compile them with minor issues. In November 2025 the repository got an MIT license. The parser, in <code>gparser.zil</code>, is the file I'd open first.</p><h3 id="20-farbrausch-tools-2001-2011">20. Farbrausch tools 2001-2011</h3><p><a href="https://github.com/farbrausch/fr_public">github.com/farbrausch/fr_public</a></p><p>Ten years of tools from Farbrausch, the German demoscene group, released as they were. The README is upfront: "This is not cleaned up. This is the raw deal, some from old hard drives, some fresh from various SVN repositories." Inside are Werkkzeug3, used for many of their demos and intros, the V2 synthesizer behind all their intros plus kkrieger and debris, the kkrunchy executable packer, and the data files for several productions. Everything is under a BSD license or in the public domain, and the main branch builds with Visual Studio 2010. If you've ever watched debris and wondered how, the answer is in there.</p><h3 id="bonus-tabs">Bonus tabs</h3><p>A few more I refuse to close:</p><p><a href="https://github.com/jansc/ncgopher">ncgopher</a> · A Gopher, Gemini and Finger client in Rust with an ncurses interface.</p><p><a href="https://github.com/theobori/ange">ange</a> · A Gopher webring built on fleur (entry 06), with its members kept in SQLite.</p><p><a href="https://github.com/ioccc-src/winner">IOCCC winners</a> · Every winning entry of the International Obfuscated C Code Contest, which started in 1984.</p><p><a href="https://github.com/kognise/overengineering">overengineeRING</a> · A webring of people who make things. You join with a pull request and a small YAML file.</p><p><a href="https://github.com/kantord/blogtato">blogtato</a> · A command-line RSS and Atom reader inspired by Taskwarrior, with sync through git.</p><p><a href="https://github.com/antonmedv/gitmal">Gitmal</a> · Turns a Git repository into static HTML pages with files, commits and syntax highlighting.</p><p><a href="https://github.com/xxh/xxh">xxh</a> · Takes your favorite shell with you over SSH, without installing anything on the host.</p><p>That's issue six. Half of it talks to servers most people have never heard of, and the other half boots machines nobody sells anymore.</p><p>Found a strange repository, a forgotten tool or a small piece of software I should see? Send it my way. Reader suggestions are welcome for issue seven. And if you make something with any of these, I'd like to see it.</p><p>Happy wandering!<br>Pablo</p>]]></content>
    </entry>
    <entry>
        <title>A short history of pubnixes and tildes</title>
        <link href="https://pablomurad.com/history-of-pubnixes-and-tildes/"/>
        <id>https://pablomurad.com/history-of-pubnixes-and-tildes/</id>
        <published>2026-11-12T02:41:16-03:00</published>
        <updated>2026-11-12T02:41:16-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="pubnix"/>
        <summary>From dial-in Unix machines in 1982 to SDF, tilde.club, tilde.town and the tildeverse: the story of public computers shared by strangers, why the tilde means home, what people do on them, and how runv.club came back to join the neighborhood.</summary>
        <content type="html"><![CDATA[<p>Long before anyone had a profile, people shared computers. Not in the sense of passing a laptop around: dozens or hundreds of people logged into the same machine at the same time, each with a home folder, a mailbox and a few programs for talking to whoever else was connected. Some of those machines were open to the public. They're called pubnixes, short for public access Unix systems, and the newer ones are usually called tildes.</p><p>This is their story, from a terminal in 1976 to a Debian box in Brazil in 2026.</p><h2 id="why-a-tilde">Why a tilde?</h2><p>Start with a keyboard. In 1976, Lear Siegler released the ADM-3A, a simple and cheap video terminal. On its keyboard, the key in the top right corner said <strong>Home</strong>, and printed on that same key was the <code>~</code> symbol.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/iticnt1ryox291qaxu6ccgrqk88k/adm-3a-keyboard-layout.png" alt="A diagram of the Lear Siegler ADM-3A terminal keyboard, black keys on white. In the top right corner, the key labeled &quot;Home&quot; also carries the tilde and caret symbols. The H, J, K and L keys have small arrows printed under the letters: left, down, up and right." width="1920" height="647" loading="lazy" srcset="https://pablomurad.com/img/fa-qMEuGdD-480.webp 480w, https://pablomurad.com/img/fa-qMEuGdD-800.webp 800w, https://pablomurad.com/img/fa-qMEuGdD-1200.webp 1200w, https://pablomurad.com/img/fa-qMEuGdD-1600.webp 1600w, https://pablomurad.com/img/fa-qMEuGdD-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>That's a common explanation for why, in Unix shells, <code>~</code> means your home directory. The same terminal left another mark that's still around: the H, J, K and L keys had arrows printed on them, and that's why vi, and later Vim, move the cursor with those four letters.</p><p>From there, the rest follows. On Unix, <code>~pablo</code> means Pablo's home folder. When the web arrived, servers started publishing a folder inside each user's home at addresses like <code>server.edu/~pablo</code>. For years, that was how a regular person put a page online: an account on a shared machine and a tilde in the address. Paul Ford, who comes back later in this story, put it well: the <code>~</code> is "a shortcut that says: 'Here is a person.'"</p><h2 id="1982-dial-in-and-wait-your-turn">1982: dial in and wait your turn</h2><p>The first public access Unix systems appeared in 1982: M-Net, in Ann Arbor, Michigan, and Chinet, in Chicago. By the late 1980s there were more than 70 of them.</p><p>There was no internet for regular people yet. You dialed the machine directly with a modem, and the number of people who could be online at the same time was the number of phone lines the system had. If there was only one modem, you kept redialing until whoever was connected logged off.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/dvd6rq5nbiu2uac2ohgc6wgckx4a/dec-vt101-adventure.jpg" alt="A beige DEC VT101 video terminal with its black keyboard, photographed from the front. The screen glows blue-white with lines from the text game Colossal Cave Adventure, such as &quot;You are crawling over cobbles in a low passage&quot; and &quot;There is a small wicker cage discarded nearby.&quot;" width="1920" height="1607" loading="lazy" srcset="https://pablomurad.com/img/zsNi24q0zW-480.webp 480w, https://pablomurad.com/img/zsNi24q0zW-800.webp 800w, https://pablomurad.com/img/zsNi24q0zW-1200.webp 1200w, https://pablomurad.com/img/zsNi24q0zW-1600.webp 1600w, https://pablomurad.com/img/zsNi24q0zW-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Long-distance calls were expensive, so most users lived in the same area code as the machine. The people you met online could literally live down the street, and sometimes you ended up meeting them in person. People used these systems mostly for message boards, mail and chat, close cousins of the BBSes of the same era, but running Unix, with all its tools available to anyone curious enough to learn them.</p><p>M-Net is still alive. In February 2025, SDF announced it had taken over the stewardship of the system, which it called the longest running public access Unix system.</p><h2 id="sdf-the-survivor">SDF, the survivor</h2><p>SDF deserves its own chapter. It started in 1987 as a bulletin board on an Apple IIe, run by Ted Uhlemann for a group of Japanese anime fans. The name comes from the anime <em>Super Dimension Fortress Macross</em>. In 1990 it became a Unix system, and since then it has never stopped.</p><p>It went through almost everything a computer can go through. In 1997 it had around 15,000 users and moved to Linux. Between 2000 and 2001 it switched to NetBSD on DEC Alpha machines and moved from Texas to Seattle. In 2001 it became a nonprofit. In May 2016 it had 47,572 registered users, and in 2018 it was the largest NetBSD installation in the world.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/k5ya7r85tepbw1ugytbrsuhdasti/sdf-org-home.png" alt="The sdf.org home page on a white background. The header reads &quot;[ SDF Public Access UNIX System .. Est. 1987 ]&quot; above two rows of blue links (join, welcome, faq, gopher, tilde, telnet, ssh and more). Below, under the line &quot;a community platform for inspiring, facilitating and implementing new ideas&quot;, sit big colored blocks: orange &quot;Signup for Shell Access&quot;, pink &quot;Join Mastodon&quot; and teal &quot;Explore Vintage Systems&quot;. The footer says &quot;©1987-2065&quot; and &quot;this page was generated using ksh, sed and awk&quot;." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/WbDuo8cPdW-480.webp 480w, https://pablomurad.com/img/WbDuo8cPdW-800.webp 800w, https://pablomurad.com/img/WbDuo8cPdW-1200.webp 1200w, https://pablomurad.com/img/WbDuo8cPdW-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Today SDF offers a free shell account, web pages, Gopher, Mastodon, a gallery and even vintage systems you can log into, like TOPS-20 and ITS. Its home page has a copyright line that runs until 2065, and a note at the bottom says it was "generated using ksh, sed and awk."</p><h2 id="the-quiet-years">The quiet years</h2><p>In the 1990s, two things happened. Linux appeared in 1991, and suddenly anyone with an old PC could run their own Unix server. And the commercial internet arrived, with millions of new people online.</p><p>That was good and bad for pubnixes. Access got easier, but abuse came with it: spam, attacks on other machines, illegal files. Many systems had to close down services that used to be open, like outgoing connections and, in some cases, email. Then the personal computer won, people moved their lives to Windows and Mac, and later to social networks. A handful of systems kept going, like SDF, Grex and Nyx, but for about two decades pubnixes were mostly a thing for people who already knew them.</p><h2 id="2014-a-couple-of-drinks-and-a-thousand-nerds">2014: a couple of drinks and a thousand nerds</h2><p>On a night in September 2014, the writer Paul Ford was reading a 2004 post by Stevie Nicks and noticed she used tildes all over the place. He joked about it on Twitter. A friend replied that tildes were only ever properly used in front of usernames on shared hosting. A few drinks in, Ford registered tilde.club, promised a shell account to anyone who wanted one, and set up the cheapest server Amazon had.</p><p>He woke up to 100 people asking for accounts. In a few days there were around 600 members, more than a thousand on the waiting list, volunteers offering to help run the machine, and, at one point, $24 in an envelope slipped under his apartment door. People made pages, a webring, lists of who was online, and a lot of ASCII art dragons.</p><p>He told the story in an essay with a great title, <a href="https://medium.com/message/tilde-club-i-had-a-couple-drinks-and-woke-up-with-1-000-nerds-a8904f0a2ebf">"I had a couple drinks and woke up with 1,000 nerds"</a>, and repeated one sentence throughout it: "Tilde.club is one cheap, unmodified Unix computer on the Internet."</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/791jtj1a6dyt74z4htc891e53uwm/tilde-club-home.png" alt="The tilde.club home page in orange monospaced text on a dark brown background, styled like a terminal. A top bar has the links ~TILDE.CLUB~, HOME, WIKI, TILDEFORGE, DONATE, CHAT and SIGN-UP. The heading &quot;$ WELCOME TO TILDE.CLUB&quot; ends in a blinking block cursor, followed by a row of &quot;Currently Active Users&quot;. On the left, a box explains that tilde.club gives you access to a shared Unix computer where you can make web pages and learn; on the right, a news post says the system now runs Fedora 43." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/TQJF01TTHc-480.webp 480w, https://pablomurad.com/img/TQJF01TTHc-800.webp 800w, https://pablomurad.com/img/TQJF01TTHc-1200.webp 1200w, https://pablomurad.com/img/TQJF01TTHc-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Twelve years later, tilde.club is still online, now running Fedora 43, with a list of active users at the top of the home page.</p><h2 id="the-town-and-the-tildeverse">The town and the tildeverse</h2><p>tilde.club closed signups fast, and that's how tilde.town was born. Its creator, ~vilmibm, had an account on tilde.club, but by the time ~vilmibm told some friends about it, there was no room left. A few days later, on October 11, 2014, came ~vilmibm's birthday, and the present on the wish list was a social Unix server. A friend stayed up all night helping with the setup. The idea was to keep the spirit of tilde.club, but make a place friendlier to less technical people, and one that would never have a user limit.</p><p>The town grew up. Today it runs on a physical server, pink and covered in stickers and graffiti, in a data center on the outskirts of Ottawa. Its members have connected from strange places, like a Sega Dreamcast:</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/lvfgm91d0uts9eldd45b1v77mhdv/tilde-town-dreamcast-irc.png" alt="A short IRC log from the #tildetown channel in pastel colors on a dark background. A user named abraxas_ joins and writes &quot;omfg&quot;, &quot;it:s happening&quot;, &quot;im on tilde on my dreamcast&quot; and &quot;this is amazing&quot;, while another user, nilaky, answers &quot;abraxas_: ?&quot; and then &quot;hahaha nice!&quot;." width="584" height="123" loading="lazy" srcset="https://pablomurad.com/img/vUJeQQNk7T-480.webp 480w, https://pablomurad.com/img/vUJeQQNk7T-584.webp 584w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Or Windows 98, which, according to the town's founder, is "the finest OS":</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/jfkt4ckvh1l847x1l9wv35ltegtc/tilde-town-windows-98.png" alt="A screenshot of Windows 98 with Internet Explorer open at http://tilde.town/. The page has a lilac background, the words tilde.town drawn in ASCII art, links for sign up, faq, guestbook, code of conduct, help ticket, donate and wiki, and the line &quot;80 townies active right now&quot;. A Windows 98 Control Panel window overlaps the browser, and the taskbar shows 12:15 AM." width="1028" height="771" loading="lazy" srcset="https://pablomurad.com/img/_2Wk-ail_W-480.webp 480w, https://pablomurad.com/img/_2Wk-ail_W-800.webp 800w, https://pablomurad.com/img/_2Wk-ail_W-1028.webp 1028w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Other tildes kept appearing, and in time they found each other. The <a href="https://tildeverse.org">tildeverse</a> is a loose association of these communities. Membership mostly means hanging out on the shared IRC network, tilde.chat, and there's a wiki anyone with an account on a recognized tilde can edit. Each server stays independent.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/fwnzzvcg8a3n8p0js8tvbtwp0cqt/tildeverse-org.png" alt="The tildeverse.org home page in green monospaced text on black, with a leafy green logo next to the title. The text says it's a loose association of like-minded tilde communities, that tildes are pubnixes in the spirit of tilde.club, created in 2014 by paul ford, and points to an IRC network and a wiki. Under &quot;members&quot;, green buttons list aussies.space, cosmic.voyage, ctrl-c.club, envs, rw.rs, texto-plano.xyz, tilde.club, tilde.guru, tilde.institute, tilde.pink, tilde.team, tilde.town and tilde.green." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/sz2cyB03pc-480.webp 480w, https://pablomurad.com/img/sz2cyB03pc-800.webp 800w, https://pablomurad.com/img/sz2cyB03pc-1200.webp 1200w, https://pablomurad.com/img/sz2cyB03pc-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The member list today includes tilde.club, tilde.town, tilde.team, ctrl-c.club, envs.net, cosmic.voyage, texto-plano.xyz and rw.rs, which showed up in Neat Finds #02 with its sign-up by pull request. Outside the tildeverse there are many more, and each one has its own personality.</p><h2 id="what-you-actually-do-on-one">What you actually do on one</h2><p>Less than you'd think, and more than it seems. You connect with SSH, and you're on a real computer, with other people. Type <code>who</code> and see who's logged in. Use <code>finger</code> to read someone's plan. Write a page by hand in <code>~/public_html</code> and see it online a second later. Join the IRC channel and talk to people who are on the same machine as you.</p><p>Every tilde grows its own little traditions. On tilde.town there's <code>feels</code>, a blogging platform that runs in the terminal, which showed up in Neat Finds #05. Other places have shared gardens, webrings, collaborative fiction, games and bots. A lot of people also keep a Gopher hole or a Gemini capsule alongside their web page.</p><p>And it's a very good way to learn. Nobody grades you, nobody's watching your metrics, and the worst that can happen is a broken <code>index.html</code>.</p><h2 id="and-then-there-s-runv">And then there's runv</h2><p><a href="https://runv.club">runv.club</a> is our pubnix, a small public Unix server run from Brazil, on Debian.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/aav1e7niekhyyq2gspzmmvay9ond/runv-club-home-nov-2026.png" alt="The runv.club home page: plain black serif text on white. It starts &quot;runv.club is a small public Unix server, run from Brazil. Members get an account on a shared Debian box and a corner of the small internet to go with it.&quot; Under &quot;What you get&quot; comes a list: a shell over SSH or mosh with many languages, a web page at runv.club/~you with a Gemini capsule and a Gopher hole, a blog that also appears on the planet, a spot on the runv ring, a status line and a /now page, a photo gallery that strips location data, your own address at you.runv.sh, an eepsite on I2P, git, bookmarks and an RSS reader, and #runvclub on tilde.chat." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/MjKmybcC7n-480.webp 480w, https://pablomurad.com/img/MjKmybcC7n-800.webp 800w, https://pablomurad.com/img/MjKmybcC7n-1200.webp 1200w, https://pablomurad.com/img/MjKmybcC7n-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The first version was online for a year and a half and closed in October 2026. Instead of burying it, the members decided together to start over, and I wrote about <a href="https://pablomurad.com/why-we-brought-runv-club-back/">why we brought runv.club back</a>. The new server is built from a repository with Ansible, the same way every time, with limits for every account and backups that go out to another machine.</p><p>It keeps the classic pieces: a shell over SSH or mosh, a page at <code>runv.club/~you</code>, a Gemini capsule, a Gopher hole, finger, twtxt, a forum in the terminal and #runvclub on tilde.chat. Since the relaunch, it has picked up a few new tricks:</p><ul>
<li value="1">a blog that publishes from <code>~/blog</code> to the web, Gemini and Gopher at once, and joins the <a href="https://runv.club/planet/">planet</a> with everyone's posts;</li>
<li value="2">a status line, like <code>status "making soup"</code>, and a <code>/now</code> page;</li>
<li value="3">a photo gallery that removes location and camera data from your pictures;</li>
<li value="4">your own address at <code>you.runv.sh</code>, for an app on the server or a tunnel from your laptop;</li>
<li value="5">an email address <code>you@runv.club</code>, forwarded to the inbox you already use;</li>
<li value="6">proofs that your Mastodon, GitHub or own site belong to you, checked by the server;</li>
<li value="7">an account on lemmy.club, a forum federated with the rest of the fediverse.</li>
</ul><p>The planet already has its first posts. Every request to join is read by a person, and we let in five people a week, so the place grows slowly. You can ask at the <a href="https://runv.club/join">join page</a> or straight from a terminal:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>ssh join@runv.club</span></span></code></pre></div><p>runv isn't the only one I look after. There's also noctem.cafe, a quiet OpenBSD machine, and Noctem 9, which runs Plan 9. I wrote about all three in <a href="https://pablomurad.com/three-small-computers-looking-for-neighbors/">Three small computers looking for neighbors</a>. And if you'd like to build your own, there's a two-part guide, <a href="https://pablomurad.com/build-your-own-tilde-part-1/">Build your own tilde</a>.</p><h2 id="pick-a-machine">Pick a machine</h2><p>Forty-four years after M-Net and Chinet, the idea is still the same: one computer, a lot of people, and a folder with your name on it. If you've never had a shell account, pick a pubnix whose rules and mood you like, read the code of conduct, make an SSH key and ask for a spot. Then type <code>who</code> and say hello to whoever else is there.</p>]]></content>
    </entry>
    <entry>
        <title>Primer, explained as much as it can be</title>
        <link href="https://pablomurad.com/primer-explained/"/>
        <id>https://pablomurad.com/primer-explained/</id>
        <published>2026-11-11T19:09:00-03:00</published>
        <updated>2026-11-11T19:13:55-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="movies"/>
        <summary>Primer cost $7,000 and is still one of the hardest time travel films to follow. How the box works, the week day by day, the twist with the three Aarons, the main theories, the real science in the garage and why programmers will recognize half of it.</summary>
        <content type="html"><![CDATA[<p>Primer is 77 minutes long, cost about $7,000 to make, and has a reputation as one of the most confusing time travel films ever made. It trusts you completely, and nobody in it stops to explain anything. You follow two engineers around, overhear their conversations, and by the last half hour you're trying to work out which copy of which character is on screen.</p><p>This post is an attempt to untangle it. The first part has no real spoilers. After that, everything is on the table: the box, the week, the twist, the theories and the bits of science and computing hiding in the dialogue. If you haven't seen the film, watch it first and come back. Turn on the subtitles, because some of the most important lines are mumbled.</p><h2 id="a-film-made-by-an-engineer-for-almost-nothing">A film made by an engineer, for almost nothing</h2><p>Primer was written, directed, produced, edited and scored by Shane Carruth, who also plays Aaron, one of the two leads. Before making films, Carruth had a degree in mathematics and worked as an engineer developing flight simulation software. He studied physics while writing the script.</p><p>The numbers are part of the legend:</p><ul>
<li value="1">a budget of $7,000;</li>
<li value="2">a crew of five people;</li>
<li value="3">five weeks of shooting on the outskirts of Dallas, Texas, on Super 16mm film;</li>
<li value="4">a shooting ratio of 2:1, which means almost every take ended up in the film;</li>
<li value="5">two years of post-production, during which Carruth says he nearly gave up several times.</li>
</ul><p>Most of the cast were his friends and family. He cast himself as Aaron because he had trouble finding actors who would stop "filling each line with so much drama." The time machine is a plain grey box, and its hum is a mix of a mechanical grinder and a car engine, with no digital effects.</p><p>In 2004 it won the Grand Jury Prize for drama at Sundance. It also won the Alfred P. Sloan Prize, which Sundance gives to films that deal with science and technology.</p><h2 id="the-setup">The setup</h2><p>Aaron and Abe are engineers who, with two friends, build electronic gadgets in Aaron's garage on their own time, hoping one of them will sell. While working on a device meant to reduce the weight of objects, they notice something strange about what they've built.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/lvi9qph2xn32ikw48adaef3eodi1/primer-garage.jpg" alt="A black and white still from Primer: Abe and Aaron, two young men in white shirts and ties, stand in a garage. Abe holds something small up to look at it, above a homemade contraption of metal tubes and parts." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/oN1WsmbWv4-480.webp 480w, https://pablomurad.com/img/oN1WsmbWv4-800.webp 800w, https://pablomurad.com/img/oN1WsmbWv4-1200.webp 1200w, https://pablomurad.com/img/oN1WsmbWv4-1600.webp 1600w, https://pablomurad.com/img/oN1WsmbWv4-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The dialogue in these scenes is dense on purpose. The characters talk the way engineers talk to colleagues who already know the project: half sentences, jargon, no explanations. Carruth has said he wanted the audience to feel as lost as the characters are. The real subject, in his words, is the relationship between the two men breaking down because neither of them can handle what the machine gives them.</p><p>From here on, spoilers.</p><h2 id="how-the-box-works">How the box works</h2><p>The box was supposed to make things lighter. What it actually does is create a loop in time. When you switch it on, the inside of the box starts running forward like everything else. When you switch it off, the inside runs backward to the moment it was switched on, then forward again, back and forth, as long as anything is in there.</p><p>Abe figures this out the hard way. After repeated tests with a small toy, a fungus grows on it, and a lab tells him the fungus is ordinary but the amount of growth means years have passed, not days. He tries again with his wristwatch and confirms it.</p><p>The trick to traveling with it is simple and uncomfortable:</p><ol>
<li value="1">In the morning, you set a box to switch on at, say, 8:45, then go hide somewhere so you don't run into anyone.</li>
<li value="2">In the afternoon, you come back and switch the box off.</li>
<li value="3">You climb inside and wait as many hours as went by, six in the film, while the box runs backward.</li>
<li value="4">You climb out at 8:45 in the morning. There are now two of you, and the other one is the version that is going to hide all day and climb into the box later.</li>
</ol><p>If everything goes right, the other version climbs into the box in the afternoon and vanishes into the past, and you are the only one left. The rule they repeat is that each box can only be used once: you can't climb out of a box and then use the same box again, because your earlier self is already the one doing that.</p><h2 id="the-week">The week</h2><p>The film covers about a week in March, starting on a Monday.</p><p>Abe tells Aaron he has already done the trip once. They build a second box and start repeating the same day over and over, hiding in a hotel room and using what they know to make same-day stock trades. They try hard to change as little as possible, because the worst outcome would be failing to send their doubles back and getting stuck with two of each of them.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/or596t0sxvh8rypoq4a7mzzxgbuv/primer-kitchen-table.jpg" alt="A still from Primer: Abe and Aaron, in white shirts and ties, stand side by side in a dim room among other people, looking off to one side. Aaron holds his hand to his ear, where he wears an earpiece." width="2443" height="1374" loading="lazy" srcset="https://pablomurad.com/img/fVFNao0tmn-480.webp 480w, https://pablomurad.com/img/fVFNao0tmn-800.webp 800w, https://pablomurad.com/img/fVFNao0tmn-1200.webp 1200w, https://pablomurad.com/img/fVFNao0tmn-1600.webp 1600w, https://pablomurad.com/img/fVFNao0tmn-2443.webp 2443w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Cracks show up fast. Aaron gets reckless and starts imagining what else they could do. He has an ear bleed. Their handwriting gets worse. On Thursday, Aaron's phone rings in his hand while his other self, in the hotel, has the same phone, and they realize that history isn't fixed: it can be changed.</p><p>On Friday night, things get stranger. They notice Thomas Granger, the father of Abe's girlfriend Rachel and the man funding the project, following them. He has a beard he didn't have the day before. When Abe calls Granger, the real one answers from home. This Granger came from the future, using one of the boxes, for reasons nobody ever explains. When Aaron gets close to him, Granger collapses into a coma.</p><p>That's when Abe loses his nerve and reveals his secret: a failsafe. Early on Monday, before he ever showed the machine to Aaron, he had switched on a third box and left it running in a storage unit. It has been on for almost four days. He climbs in with oxygen and water, rides it all the way back to Monday morning, drugs his past self so that self never discovers anything, and goes to meet Aaron to stop the whole thing before it starts.</p><h2 id="the-twist-aaron-was-always-ahead">The twist: Aaron was always ahead</h2><p>This is the part where almost everybody gets lost on a first watch.</p><p>When Abe arrives at the bench on Monday, Aaron is already there, with an earpiece in his ear, saying the same lines as the first time. When Abe passes out, it turns out Aaron isn't listening to a basketball game. He's listening to a recording of their own conversation. This Aaron has done all of this before.</p><p>Aaron found Abe's failsafe much earlier and kept quiet about it. He built boxes of his own, went back in time, drugged his original self and locked him in the attic (the noise his wife Kara mentioned), and lived the week wearing an earpiece with recordings, so he could repeat every conversation. He also discovered that boxes can be "recycled": you can't reuse the box you came out of, but you can carry a folded box with you, switch it on at the other end and use it later to go back again.</p><p>The usual reading of what we see is that there are three Aarons by the end. The original, drugged in the attic. A second one, often called Hooded Aaron, who leaves town. And the one we've been following since the bench scene, who is already the third.</p><p>Why go to all this trouble? The answer seems to be Rachel. At a birthday party on Monday night, her ex-boyfriend shows up with a shotgun. The film implies that in an earlier timeline this went badly, and that Aaron has been reliving those days to fix it. Back on Monday, the two of them work together to make the party end well, with Aaron stopping the gunman and becoming a local hero.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/cw0vtafxgh6157murudmdwv4qewl/primer-exhaustion.jpg" alt="A still from Primer: a long, bright office corridor with floor-to-ceiling glass windows on the left, and two men in white shirts standing far apart near the far end." width="2880" height="1620" loading="lazy" srcset="https://pablomurad.com/img/ZWG17q8IC--480.webp 480w, https://pablomurad.com/img/ZWG17q8IC--800.webp 800w, https://pablomurad.com/img/ZWG17q8IC--1200.webp 1200w, https://pablomurad.com/img/ZWG17q8IC--1600.webp 1600w, https://pablomurad.com/img/ZWG17q8IC--2880.webp 2880w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="the-ending">The ending</h2><p>Abe and Aaron go their separate ways. Abe stays behind to watch over his original self and sabotage any attempt to build the machine again. Aaron, who can never go home because his family now belongs to the original Aaron, leaves the country. In the last scene, an Aaron is somewhere far away, directing French-speaking workers as they build a box the size of a warehouse.</p><p>The voice-over that runs through the film turns out to be a phone call: one of the Aarons telling the whole story to his original self. Near the end, he says: "You will not be contacted by me again."</p><h2 id="the-theories">The theories</h2><p>Primer doesn't answer every question, and fans have spent two decades filling the gaps. These are the most common readings:</p><ul>
<li value="1">
<strong>The narrator is Hooded Aaron.</strong> The voice-over is the phone call from the Aaron who left town, explaining everything to the original Aaron he drugged. It's the most accepted reading.</li>
<li value="2">
<strong>The title is the call.</strong> A primer is a first coat of paint, or an introductory book. One popular theory says the "primer" is the phone call itself: the instructions one Aaron leaves for another to start the whole cycle again.</li>
<li value="3">
<strong>Granger's trip is never explained.</strong> Something in the future was bad enough to make him go back and follow the two of them. Whether that disaster was avoided, the film doesn't say.</li>
<li value="4">
<strong>History can change.</strong> Unlike films where the past is fixed and every trip was "always" part of history, Primer shows timelines being overwritten. The ringing phone is the proof.</li>
<li value="5">
<strong>Time travel hurts.</strong> The ear bleeds, the bad handwriting and the near-fatal reaction between Aaron and Granger all suggest the body pays a price, especially for anyone who has spent many hours inside the boxes.</li>
<li value="6">
<strong>The warehouse.</strong> The final box is huge, which suggests that someone, somewhere, is planning something much bigger than stock trades.</li>
</ul><p>Carruth once pointed to where some of the ideas came from: "Richard Feynman has some interesting ideas about time."</p><h2 id="the-science-in-the-garage">The science in the garage</h2><p>The dialogue is full of real science, even if the machine is fiction.</p><p>The box needs <strong>superconductivity</strong>. A superconductor is a material that carries electricity with zero resistance, and it does something odd with magnetic fields: it pushes them out, which is why a magnet can float above a cooled superconductor. That's called the Meissner effect. The catch is temperature. Every known superconductor needs extreme cold, and a superconductor that works at room temperature, which is what the characters joke about building, remains one of the great unsolved problems in physics. In 2023, a material called LK-99 caused a frenzy online after a claim that it was exactly that, until labs around the world tried to replicate it and found it wasn't.</p><p>For their homemade version, they pull <strong>palladium</strong> out of a car's catalytic converter. Palladium is a real catalyst metal, and it's in catalytic converters for exactly that reason.</p><p>The box is sealed and filled with <strong>argon</strong>, a noble gas that barely reacts with anything. It's used in welding and in light bulbs for the same reason: to keep everything inside stable.</p><p>And the fungus is a beautiful detail: Abe uses biology as a clock. The amount of growth tells him how much time really passed inside the box, the same reasoning scientists use when they date things by measuring something that changes at a known rate.</p><p>The Feynman quote points to an idea from particle physics. In Feynman's diagrams, an antiparticle moving forward in time can be described as a particle moving backward in time. The box's back-and-forth loop between two moments has the same flavor.</p><h2 id="what-programmers-will-recognize">What programmers will recognize</h2><p>Anyone who works with computers will find a lot of familiar ideas in the box.</p><ul>
<li value="1">
<strong>Snapshot and restore.</strong> Switching on a box is taking a snapshot of a moment. Climbing in is restoring it. The box doesn't let you jump anywhere you want, only back to a point you saved earlier.</li>
<li value="2">
<strong>The failsafe is a backup.</strong> Abe's secret box, running since before the experiment, is the backup you take before a risky deploy, so you can roll back if everything goes wrong. Aaron simply had an older backup.</li>
<li value="3">
<strong>The earpiece is a replay log.</strong> Aaron records every conversation and plays it back so he can repeat his lines. It's like replaying recorded traffic against a system to reproduce the exact same behavior.</li>
<li value="4">
<strong>"Recyclable" boxes are nested snapshots.</strong> You can't restore from the same snapshot twice, but you can carry a new one with you, take it at the other end, and roll back again from there.</li>
<li value="5">
<strong>Two Aarons is split brain.</strong> In a cluster, split brain is when two nodes both think they're the primary and start making decisions independently. That's exactly what happens to Aaron, and to Abe, and their lives never merge back.</li>
<li value="6">
<strong>The phone call is a race condition.</strong> Two copies of the same phone, one event, and only one of them can ring. The moment the "wrong" one rings, the state diverged.</li>
<li value="7">
<strong>The muffin.</strong> On the second pass through Thursday, Abe eats a muffin while Aaron watches a game they already know the result of. It's the most relatable moment of time travel in the film: once you've replayed the day, you start optimizing small things.</li>
</ul><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/vk0mbnsfrxj7bpzvun4hbdli8doc/primer-muffin.jpg" alt="A still from Primer: a close-up of Abe in profile, a young man with short light hair in a dark jacket, looking ahead with a serious expression, in warm, soft light." width="2880" height="1620" loading="lazy" srcset="https://pablomurad.com/img/UDKyFm5jcy-480.webp 480w, https://pablomurad.com/img/UDKyFm5jcy-800.webp 800w, https://pablomurad.com/img/UDKyFm5jcy-1200.webp 1200w, https://pablomurad.com/img/UDKyFm5jcy-1600.webp 1600w, https://pablomurad.com/img/UDKyFm5jcy-2880.webp 2880w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="how-to-watch-it">How to watch it</h2><p>A few things make Primer much easier:</p><ol>
<li value="1">Watch with subtitles. Key lines are spoken quietly or in the background.</li>
<li value="2">Pay attention to Aaron's earpiece. Every time he's wearing it, ask yourself which Aaron he is.</li>
<li value="3">Watch the handwriting and the ear bleeds. They show who has spent the most time inside the boxes.</li>
<li value="4">Don't expect to get it all the first time. Nobody does. The film was built to be watched at least twice.</li>
</ol><p>If it makes you feel better, even xkcd gave up. In its famous chart of movie narratives, the lines for Abe, Aaron and Granger go into a giant knot of scribbles and come out the other side as question marks.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/btj5h13pubew0juc8qfa027btgyr/xkcd-657-movie-narrative-charts.png" alt="The xkcd comic &quot;Movie Narrative Charts&quot;, with hand-drawn charts showing how characters move through the plots of several films. The chart for Primer starts with three neat lines labeled Abe, Aaron and Granger, which run into a huge, dense tangle of scribbles and come out the other side as three faint lines labeled with question marks." width="3274" height="2067" loading="lazy" srcset="https://pablomurad.com/img/4BYBBhasEl-480.webp 480w, https://pablomurad.com/img/4BYBBhasEl-800.webp 800w, https://pablomurad.com/img/4BYBBhasEl-1200.webp 1200w, https://pablomurad.com/img/4BYBBhasEl-1600.webp 1600w, https://pablomurad.com/img/4BYBBhasEl-3274.webp 3274w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p><a href="https://xkcd.com/657/"><em>Movie Narrative Charts</em></a><em>, xkcd, CC BY-NC 2.5.</em></p>]]></content>
    </entry>
    <entry>
        <title>Two notebooks: Docs do Murad and Pablo, how?</title>
        <link href="https://pablomurad.com/docs-do-murad-and-pablo-how/"/>
        <id>https://pablomurad.com/docs-do-murad-and-pablo-how/</id>
        <published>2026-11-11T11:23:31-03:00</published>
        <updated>2026-11-11T11:23:31-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="projects"/>
        <summary>Two places where the writing that doesn’t fit a blog lives: Docs do Murad, a compendium of recipes in Portuguese for servers, privacy and the web by hand, and Pablo, how?, a digital garden in English of fixes, concepts and lists.</summary>
        <content type="html"><![CDATA[<p>This blog is where I tell stories. But a lot of what I write never fits a blog post: the exact steps to bring up a server, the script that fixed a certificate, the short explanation of a concept that finally clicked. That writing lives in two other places, and they do different jobs. One is a compendium of recipes in Portuguese. The other is a notebook in English.</p><h2 id="docs-do-murad-the-recipe-shelves">Docs do Murad, the recipe shelves</h2><p><a href="https://docs.pablomurad.com">Docs do Murad</a> is where I record, in Brazilian Portuguese, how I set up and keep running the things I use every day: the VPS, Docker, the servers at home, privacy, websites written by hand. The home page calls it a compendium, a catalog of recipes, and makes a point of saying what it isn't: not a feed and not a garden of drafts.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/9r0dmlk7p7rhj9ili9fcomhaesqc/docs-do-murad-home.png" alt="The home page of Docs do Murad in a dark theme, with a cartoon cat as the logo, a search box and a top menu with Início, Essenciais, Serviços, Casa, Privacidade, Web à mão, Trecos, Papéis and Sobre. The page, titled &quot;Bem-vindos(as)&quot;, explains in Portuguese that this is where Pablo records how he set up and runs what he uses every day, and that it is a compendium, a catalog of recipes. Below, cards under &quot;Por onde começar&quot; point to a new VPS, setting up a service, the home servers and privacy." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/sQNZbxoExx-480.webp 480w, https://pablomurad.com/img/sQNZbxoExx-800.webp 800w, https://pablomurad.com/img/sQNZbxoExx-1200.webp 1200w, https://pablomurad.com/img/sQNZbxoExx-1600.webp 1600w, https://pablomurad.com/img/sQNZbxoExx-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>It's organized like a set of shelves:</p><ul>
<li value="1">
<strong>Essenciais</strong>: the ritual for a new VPS. Fresh Debian, Docker, Nginx and SSH closed to the street.</li>
<li value="2">
<strong>Serviços</strong>: the biggest shelf, with more than a hundred recipes sorted by what they do (media, files, notes, network, metrics), each marked with the machine it runs on.</li>
<li value="3">
<strong>Casa</strong>: the hardware at home, the machines called kaiser and nyx, and a lab with OPNsense, DNS and hostnames.</li>
<li value="4">
<strong>Privacidade</strong>: browsers, passwords, disk encryption, and how to get around CGNAT without depending on the internet provider.</li>
<li value="5">
<strong>Web à mão</strong>: making a site with plain HTML and CSS, Neocities and the small web, starting from a name and a folder.</li>
<li value="6">
<strong>Trecos</strong>: the drawer, for everything that doesn't fit anywhere else.</li>
<li value="7">
<strong>Papéis</strong>: opinion and longer pieces, from BSD to GrapheneOS to the craft itself.</li>
</ul><p>There's also a glossary for words like POSIX, TTY and cgroup, a page of references, an index by tag and a changelog. The changelog describes itself well: not a blog, a log of the shelves. When a page moves to a new shelf, the old address redirects, so links don't break.</p><p>The recipes try to be honest about their limits. The first one, about preparing a new server, explains why Debian ("being able to repeat the setup and understand the services that are running weighs more than installing the novelty of the week"), and the certificate script that comes with it lists exactly what it doesn't handle, so nobody runs it blind. Some texts are mine and some are adapted from other writers, with credit on each page.</p><p>A few more details. It's built with Zensical and comes to close to 300 Markdown pages. Every page carries tags asking AI crawlers not to train on it. And the site has a mascot, Netuno, a cat who, according to the home page, thinks he has sudo.</p><h2 id="pablo-how-the-notebook">Pablo, how?, the notebook</h2><p><a href="https://pablo.how">Pablo, how?</a> is the English side, and a different animal. It's a digital garden: short notes that start small, grow, pick up links to other notes and sometimes get renamed. Nothing there is finished, on purpose.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/gt4lu4gayftntxglx2mvlrwadjz2/pablo-how-home.png" alt="The home page of &quot;Pablo, how?&quot;, a light, clean page published with Obsidian Publish. A sidebar on the left has a search box and the sections Home, Concepts, How I, Lists and Web by hand. In the middle, the text begins: &quot;I've been taking things apart to see how they work since I was a kid&quot;, mentions Soledade de Minas, a Pentium MMX in 1997, Delphi, Linux, IRC and Python, and then explains that this is a digital garden of short notes. On the right, a small interactive graph of linked notes and a list of the headings on the page." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/CEuAttt6dp-480.webp 480w, https://pablomurad.com/img/CEuAttt6dp-800.webp 800w, https://pablomurad.com/img/CEuAttt6dp-1200.webp 1200w, https://pablomurad.com/img/CEuAttt6dp-1600.webp 1600w, https://pablomurad.com/img/CEuAttt6dp-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The name comes from the question. It's where I write down how I do things: the server at home, the bug that took too long to find, the concept that finally clicked, the tool I use and why. The home page also tells the long version of how I got here, from the electronics I opened as a kid in Soledade de Minas to the Pentium MMX that showed up in 1997, and from Delphi to Linux, IRC and Python.</p><p>The notes come in four kinds:</p><ul>
<li value="1">
<strong>How I did it</strong>: real fixes, with the mess included. Taming an unstable i9-14900K, finding out why my GoToSocial RSS feed was empty, streaming from behind CGNAT, running a 24/7 TV channel from home.</li>
<li value="2">
<strong>Concepts</strong>: things explained in plain words. Nostr, IPFS, microformats, LoRa, textcasting, Creative Commons, plus a short history of the Brazilian internet and a look at the operating systems of North Korea.</li>
<li value="3">
<strong>Lists</strong>: pubnixes worth an account, Gemini capsules worth a visit, games you play in a terminal, small email providers worth paying for, and, because it's my garden, horror films few people have seen.</li>
<li value="4">
<strong>Web by hand</strong>: a seven-part series that takes a site from an empty folder to a styled page with its own RSS feed, using only HTML and CSS.</li>
</ul><p>It runs on Obsidian Publish, so every page has a small interactive graph of the notes around it, which is a fun way to wander. The Web by hand series even admits it on its first page: "I'm aware of the irony of writing a series about doing it all by hand." There are more than seventy notes so far.</p><h2 id="which-one-to-open">Which one to open</h2><p>If you want to do something, step by step, and you read Portuguese, go to the Docs. If you want to understand something, or see how a problem got solved, go to Pablo, how?. And if you want the story around all of it, you're already here.</p><p>The garden already links to the Docs, and this blog now links to both. Pick a shelf or a note that looks interesting and follow the links from there.</p>]]></content>
    </entry>
    <entry>
        <title>Emacs beyond the basics, part III: a real programming setup</title>
        <link href="https://pablomurad.com/emacs-beyond-the-basics-part-3/"/>
        <id>https://pablomurad.com/emacs-beyond-the-basics-part-3/</id>
        <published>2026-11-11T09:27:42-03:00</published>
        <updated>2026-11-11T09:27:42-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="emacs"/>
        <summary>Turn Emacs into a modern programming setup: projects, Eglot and language servers, tree-sitter, completion, Consult and Embark, compile and debug, Git without Magit, containers and a ready-to-paste config.</summary>
        <content type="html"><![CDATA[<p>Emacs has been a programming editor since before most programming languages we use today existed. For a while, though, setting it up for modern work meant a pile of third-party packages and a lot of patience. That changed. Since Emacs 29, the core pieces of a modern setup come in the box: a client for language servers, tree-sitter parsing and <code>use-package</code>, next to the project management that was already there. Emacs 30 and 31 filled in a lot of the gaps.</p><p>This post turns a plain Emacs into a comfortable place to write code. It assumes you've read the <a href="https://pablomurad.com/emacs-for-the-curious/">first Emacs guide</a>, or at least that <code>C-x C-f</code> and <code>M-x</code> don't scare you anymore. <a href="https://pablomurad.com/emacs-beyond-the-basics-part-2/">Part II</a> helps if you want to understand every line of the config, but you can follow along without it.</p><p>Everything here works on Emacs 29 and newer, unless I point out a version.</p><h2 id="projects">Projects</h2><p>Most of programming happens inside a project, usually a Git repository. Emacs has <code>project.el</code> built in, and it figures out the project on its own: open any file inside a Git repository and Emacs knows where the project starts.</p><p>Everything lives under <code>C-x p</code>:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-x p p: switch to another project</span></span>
<span class="line"><span>C-x p f: find a file in this project</span></span>
<span class="line"><span>C-x p g: search the whole project for a regexp</span></span>
<span class="line"><span>C-x p r: search and replace across the project</span></span>
<span class="line"><span>C-x p b: switch to a buffer from this project</span></span>
<span class="line"><span>C-x p D: open Dired at the root of the project</span></span>
<span class="line"><span>C-x p d: pick a folder inside the project and open it in Dired</span></span>
<span class="line"><span>C-x p s: open a shell at the root of the project</span></span>
<span class="line"><span>C-x p e: open Eshell at the root of the project</span></span>
<span class="line"><span>C-x p c: compile the project</span></span>
<span class="line"><span>C-x p !: run a shell command at the root</span></span>
<span class="line"><span>C-x p k: close every buffer of this project</span></span></code></pre></div><p><code>C-x p p</code> remembers every project you've opened. Pick one and Emacs asks what you want to do there: find a file, search, open a shell, and so on. Switching between three projects becomes a matter of a few keys.</p><p>If a folder isn't a Git repository but you still want Emacs to treat it as a project, you can teach it other markers, like a <code>package.json</code> or a <code>requirements.txt</code>, through the <code>project-vc-extra-root-markers</code> option.</p><h2 id="language-servers-with-eglot">Language servers with Eglot</h2><p>A language server is a program that understands one language deeply: where every function is defined, what a variable's type is, what's wrong with the line you just wrote. VS Code, Neovim and Emacs all talk to the same servers, using the same protocol. Emacs's client is called Eglot, and it's built in since version 29.</p><p>First, install the server for your language. Some common ones:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>npm install -g pyright # Python</span></span>
<span class="line"><span>go install golang.org/x/tools/gopls@latest # Go</span></span>
<span class="line"><span>rustup component add rust-analyzer # Rust</span></span>
<span class="line"><span>npm install -g typescript-language-server typescript # JavaScript and TypeScript</span></span>
<span class="line"><span>sudo apt install clangd # C and C++, or your system's package manager</span></span></code></pre></div><p>Then open a file and run <code>M-x eglot</code>. Eglot already knows which server goes with which language, so most of the time there's nothing to configure. For Python alone it knows about a long list of servers, from pyright and basedpyright to pylsp and ruff, and it picks the first one it finds installed.</p><p>From then on, these keys do the heavy lifting:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>M-.: go to the definition of what's under the cursor</span></span>
<span class="line"><span>M-,: come back</span></span>
<span class="line"><span>M-?: find every place that uses it</span></span>
<span class="line"><span>M-x eglot-rename: rename it everywhere</span></span>
<span class="line"><span>M-x eglot-code-actions: quick fixes the server suggests</span></span>
<span class="line"><span>M-x eglot-format-buffer: format the file</span></span>
<span class="line"><span>M-x flymake-show-buffer-diagnostics: list every error and warning</span></span></code></pre></div><p>The errors appear underlined as you type, through Flymake, which is also built in. The documentation of the thing under the cursor shows up at the bottom of the screen, through ElDoc. Neither needs setup.</p><p>To start Eglot automatically, add it to the hooks of the modes you use:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> eglot</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">hook</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">python-ts-mode go-ts-mode rust-ts-mode</span><span style="color:#666666">)</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-ensure</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (:</span><span style="color:#C99076">map</span><span style="color:#DBD7CAEE"> eglot-mode-map</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c e r</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-rename</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c e a</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-code-actions</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c e f</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-format-buffer</span><span style="color:#666666">)))</span></span></code></pre></div><p>The <code>:bind</code> part gives the three most useful commands short keys, under <code>C-c e</code>, only when Eglot is active. And if your server isn't on Eglot's list, you add it yourself:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">with-eval-after-load</span><span style="color:#666666"> '</span><span style="color:#C99076">eglot</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-to-list</span><span style="color:#666666"> '</span><span style="color:#C99076">eglot-server-programs</span></span>
<span class="line"><span style="color:#666666">'(</span><span style="color:#DBD7CAEE">lua-mode </span><span style="color:#CB7676">.</span><span style="color:#666666"> (</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">lua-language-server</span><span style="color:#C98A7D77">"</span><span style="color:#666666">))))</span></span></code></pre></div><h2 id="tree-sitter-syntax-that-understands-the-code">Tree-sitter: syntax that understands the code</h2><p>Old Emacs modes highlighted code with regular expressions, which works most of the time and breaks in strange ways the rest. Tree-sitter parses the file into a real syntax tree, like a compiler would. The result is faster, more accurate highlighting and indentation, and smarter movement through the code.</p><p>Each language has a mode with <code>-ts-</code> in the name: <code>python-ts-mode</code>, <code>go-ts-mode</code>, <code>rust-ts-mode</code>, <code>typescript-ts-mode</code>, <code>tsx-ts-mode</code>, <code>js-ts-mode</code>, <code>json-ts-mode</code>, <code>bash-ts-mode</code>, <code>yaml-ts-mode</code> and many more. Each one needs a grammar, a small library for that language.</p><p>On Emacs 31, this got a lot easier. One line tells Emacs to prefer the tree-sitter modes:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">setopt treesit-enabled-modes </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span></code></pre></div><p>When a grammar is missing, Emacs 31 asks if you want to install it, and does the work for you. That's the default behavior, controlled by the <code>treesit-auto-install-grammar</code> option. Building a grammar needs a C compiler on your system, so have <code>gcc</code> or <code>clang</code> around.</p><p>On Emacs 29 and 30, it takes two steps. Install the grammar with <code>M-x treesit-install-language-grammar</code>, then tell Emacs to use the new mode instead of the old one:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-to-list</span><span style="color:#666666"> '</span><span style="color:#C99076">major-mode-remap-alist</span><span style="color:#666666"> '(</span><span style="color:#B8A965">python-mode</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> python-ts-mode</span><span style="color:#666666">))</span></span></code></pre></div><p>People on those versions often use a package from MELPA called <code>treesit-auto</code> to automate this. On Emacs 31 you don't need it anymore.</p><h2 id="completion-while-you-type">Completion while you type</h2><p>Completion in the minibuffer was covered in the first guide, with Vertico, Orderless and Marginalia. Completion inside the code is a separate thing, and there are two good paths.</p><p>The built-in one, since Emacs 30, is <code>completion-preview-mode</code>. It shows the most likely completion in gray right after the cursor, and <code>TAB</code> accepts it. It's quiet and light.</p><p>If you prefer the classic popup with a list of options, use Corfu, from GNU ELPA:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> corfu</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">custom</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">corfu-auto </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">init</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">global-corfu-mode</span><span style="color:#666666">))</span></span></code></pre></div><p>With <code>corfu-auto</code>, the list shows up on its own while you type. Eglot feeds it, so you get the same completions as any other editor using that language server.</p><h2 id="finding-things">Finding things</h2><p>Programming is mostly reading, and reading is mostly searching. Consult, from GNU ELPA, gives Emacs a set of search commands with live previews:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> consult</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> ((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-x b</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-buffer</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">M-s l</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-line</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">M-s r</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-ripgrep</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">M-g i</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-imenu</span><span style="color:#666666">)))</span></span></code></pre></div><ul>
<li value="1">
<code>consult-buffer</code> replaces the buffer switcher and also lists recent files and bookmarks.</li>
<li value="2">
<code>consult-line</code> searches the lines of the current file and jumps as you move through the results.</li>
<li value="3">
<code>consult-ripgrep</code> searches the whole project with ripgrep, so install <code>rg</code> first.</li>
<li value="4">
<code>consult-imenu</code> lists every function, class and heading in the file. Without Consult, the built-in <code>M-x imenu</code> does the same in a simpler way.</li>
</ul><p>Embark, also from GNU ELPA, is the companion. Press its key on anything, a file in a list, a symbol in the code, a search result, and it shows what you can do with it: open, rename, copy, delete, search for it. If you use Consult and Embark together, also install <code>embark-consult</code>, which connects the two.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> embark</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-.</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> embark-act</span><span style="color:#666666">))</span></span></code></pre></div><p><code>C-.</code> works in graphical Emacs. In a terminal, pick another key.</p><p>One gem in Emacs 31: search results became editable. Run <code>M-x rgrep</code> or <code>M-x grep</code>, press <code>e</code> in the results buffer, and edit the lines right there. Your changes go straight into the files. <code>C-c C-c</code> finishes. Renaming a word across forty files becomes a search and a couple of keystrokes.</p><h2 id="compile-test-and-jump-to-the-errors">Compile, test and jump to the errors</h2><p><code>M-x compile</code> runs any command, <code>make</code>, <code>cargo build</code>, <code>go test ./...</code>, <code>npm test</code>, and shows the output in a buffer. Error messages become links: press <code>RET</code> on one and Emacs opens the file at that line. <code>C-x p c</code> does the same from the root of the project.</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>M-x compile: run a build or test command</span></span>
<span class="line"><span>M-x recompile: run the same command again</span></span>
<span class="line"><span>M-g n: jump to the next error</span></span>
<span class="line"><span>M-g p: jump to the previous error</span></span></code></pre></div><p><code>C-x</code> followed by the backquote key also jumps to the next error. Set <code>compilation-scroll-output</code> to <code>'first-error</code> and the output stops scrolling at the first problem, which is usually the one you want to read.</p><h2 id="running-code-interactively">Running code interactively</h2><p>Many languages have a REPL integration. Python is a good example of how they work:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c C-p: start a Python shell (run-python)</span></span>
<span class="line"><span>C-c C-c: send the whole file to it</span></span>
<span class="line"><span>C-c C-z: jump between the code and the shell</span></span></code></pre></div><p>The same idea exists for Lisp, Ruby, Scheme, shell scripts and others. Write some code, send it, look at the result, adjust. It's a different rhythm from save and run, and a pleasant one.</p><h2 id="debugging">Debugging</h2><p>For C, C++ and other languages GDB understands, Emacs has a full interface built in. Run <code>M-x gdb</code>, then <code>M-x gdb-many-windows</code>, and you get the source, the variables, the stack, the breakpoints and the GDB console laid out together.</p><p>For everything else, there's Dape, from GNU ELPA. It speaks the Debug Adapter Protocol, the same one VS Code uses for its debugger, so it works with debugpy for Python, delve for Go, codelldb for Rust and C++, and the JavaScript debuggers. Install the adapter for your language, run <code>M-x dape</code> and choose a configuration.</p><h2 id="git-besides-magit">Git, besides Magit</h2><p>Magit is still the star, and the first guide covers it. But Emacs also has a built-in version control interface, VC, which works with Git, Mercurial and others, and has quick keys for small jobs:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-x v v: do the next logical thing (add a new file, commit changes)</span></span>
<span class="line"><span>C-x v =: diff of the current file</span></span>
<span class="line"><span>C-x v D: diff of the whole repository</span></span>
<span class="line"><span>C-x v l: log of the current file</span></span>
<span class="line"><span>C-x v g: who wrote each line (blame)</span></span></code></pre></div><p>When a merge goes wrong, open the file with conflicts and Emacs turns on <code>smerge-mode</code> by itself. Its commands live under <code>C-c ^</code>: <code>C-c ^ n</code> goes to the next conflict, <code>C-c ^ u</code> keeps your version (the upper one), <code>C-c ^ l</code> keeps the other one (the lower one) and <code>C-c ^ a</code> keeps both.</p><p>And for a quiet reminder of what you've changed, <code>diff-hl</code> marks changed lines in the margin:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> diff-hl</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">init</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">global-diff-hl-mode</span><span style="color:#666666">))</span></span></code></pre></div><h2 id="small-helpers-that-add-up">Small helpers that add up</h2><p>A few built-in modes that are worth turning on in code buffers:</p><ul>
<li value="1">
<code>electric-pair-mode</code> closes brackets and quotes for you.</li>
<li value="2">
<code>subword-mode</code> makes <code>M-f</code> and <code>M-b</code> stop inside <code>camelCase</code> words.</li>
<li value="3">
<code>hs-minor-mode</code> folds functions and blocks. Its keys live under <code>C-c @</code>, and <code>C-c @ C-h</code> hides the block you're in.</li>
<li value="4">
<code>which-function-mode</code> shows the name of the current function in the mode line.</li>
<li value="5">
<code>whitespace-mode</code> shows tabs and trailing spaces, for when the linter complains about something invisible.</li>
<li value="6">
<code>display-line-numbers-mode</code>, with <code>display-line-numbers-type</code> set to <code>'relative</code> if you like counting jumps.</li>
<li value="7">
<code>editorconfig-mode</code>, built in since Emacs 30, follows the <code>.editorconfig</code> file that many projects have, so your indentation matches the team's.</li>
</ul><p>Turning them on for every programming mode is one hook each:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">display-line-numbers-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">hs-minor-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">subword-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">electric-pair-local-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">editorconfig-mode </span><span style="color:#4C9A91">1</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; Emacs 30 and newer</span></span></code></pre></div><p>For snippets, the classic package is YASnippet, from GNU ELPA. Emacs also ships simpler tools for the same job, like abbrevs: <code>M-x define-global-abbrev</code>, type a short word and its expansion, and it expands as you type once <code>abbrev-mode</code> is on.</p><h2 id="code-inside-containers">Code inside containers</h2><p>TRAMP, which the first guide used for SSH, also opens files inside containers since Emacs 29:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>/docker:container-name:/app/main.py</span></span>
<span class="line"><span>/podman:container-name:/app/main.py</span></span></code></pre></div><p>Dired, shells and <code>M-x compile</code> all work in those paths, so you can browse and build inside a container without leaving Emacs.</p><h2 id="putting-it-together">Putting it together</h2><p>Here's the programming part of a config, in one block, ready to paste after the config from the first guide:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#758575DD">;;; Programming</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> eglot</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">hook</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">python-ts-mode go-ts-mode rust-ts-mode</span><span style="color:#666666">)</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-ensure</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (:</span><span style="color:#C99076">map</span><span style="color:#DBD7CAEE"> eglot-mode-map</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c e r</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-rename</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c e a</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-code-actions</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c e f</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> eglot-format-buffer</span><span style="color:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> corfu</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">custom</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">corfu-auto </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">init</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">global-corfu-mode</span><span style="color:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> consult</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> ((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-x b</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-buffer</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">M-s l</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-line</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">M-s r</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-ripgrep</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">M-g i</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> consult-imenu</span><span style="color:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> embark</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-.</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> embark-act</span><span style="color:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> embark-consult</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> diff-hl</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">init</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">global-diff-hl-mode</span><span style="color:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> compilation-scroll-output </span><span style="color:#666666">'</span><span style="color:#C99076">first-error</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">display-line-numbers-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">hs-minor-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">subword-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">electric-pair-local-mode</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Emacs 30 and newer</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">editorconfig-mode </span><span style="color:#4C9A91">1</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Emacs 31: prefer tree-sitter modes and offer to install grammars</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">setopt treesit-enabled-modes </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span></code></pre></div><p>On Emacs 29, remove the editorconfig line. On Emacs 29 and 30, remove the tree-sitter line at the end and use <code>major-mode-remap-alist</code> for tree-sitter, as shown above. As always, select the block and press <code>TAB</code> to let Emacs indent it nicely.</p><h2 id="a-sane-way-to-adopt-all-this">A sane way to adopt all this</h2><p>Don't paste everything on a Monday morning before a deadline. Add one piece at a time, in this order: projects, then Eglot, then the search commands, then the rest. Live with each one for a few days. The pieces you actually use will stick, and the ones you don't can go.</p><p>This is part III of a series. <a href="https://pablomurad.com/emacs-beyond-the-basics-part-1/">Part I</a> is about living inside Emacs: feeds, gopher, the fediverse, chat and mail. <a href="https://pablomurad.com/emacs-beyond-the-basics-part-2/">Part II</a> teaches enough Emacs Lisp to bend all of this to your taste.</p>]]></content>
    </entry>
    <entry>
        <title>POLYGON1993 paints with broken signals</title>
        <link href="https://pablomurad.com/polygon1993-glitch-art/"/>
        <id>https://pablomurad.com/polygon1993-glitch-art/</id>
        <published>2026-11-10T20:54:35-03:00</published>
        <updated>2026-11-10T20:54:35-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="art"/>
        <summary>I&apos;ve followed POLYGON1993 for about four years. On Instagram I followed him from several of my accounts, one after another, until Instagram banned me. I love his work, and this post is about him: who he is, how those images get made and where they&apos;ve been going. If you&apos;ve never</summary>
        <content type="html"><![CDATA[<p>I've followed POLYGON1993 for about four years. On Instagram I followed him from several of my accounts, one after another, until Instagram banned me. I love his work, and this post is about him: who he is, how those images get made and where they've been going.</p><p>If you've never seen it, picture faces and bodies made of horizontal lines of light, usually an electric blue, that bend, stretch and come apart as if the signal were failing. It looks like a 90s television in its last good minutes, still showing something beautiful.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/e5s509ua1jdmgl50p6rxidfcxt1f/polygon1993-let-it-go.png" alt="A work by POLYGON1993: an old CRT television screen in a dark room shows the head and shoulders of a person in profile, drawn entirely in wavy horizontal lines of electric blue light that break apart into dots on the right side. Below the image, in pixelated white letters, are the words &quot;let it go&quot; and a blinking cursor." width="1600" height="1600" loading="lazy" srcset="https://pablomurad.com/img/cXGX2S-KLV-480.webp 480w, https://pablomurad.com/img/cXGX2S-KLV-800.webp 800w, https://pablomurad.com/img/cXGX2S-KLV-1200.webp 1200w, https://pablomurad.com/img/cXGX2S-KLV-1600.webp 1600w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="veni-vidi-glitchi">Veni, vidi, glitchi</h2><p>POLYGON1993 is a French visual artist, film director and media scenographer based in Paris. His Tumblr sums him up in three words: "Veni, vidi, glitchi."</p><p>It started with music. In a <a href="https://www.allships.co/article/interview-polygon">2021 interview</a>, he explained that in 2015 he was composing songs that were "very glitchesque" and wanted visuals with the same intensity. As a kid of the 90s he was already drawn to the look of VHS, and when he found analog glitch art, he decided to try it himself.</p><h2 id="a-television-in-the-middle-of-everything">A television in the middle of everything</h2><p>What makes his work different from a filter is the hardware. He works with CRT televisions and circuit-bent video equipment from the 80s and 90s. Circuit bending means opening an electronic device and rewiring it on purpose, so it makes errors it was never designed to make. Any image he sends through that chain, a photo, a video or a drawing, comes out the other side distorted by the machines, and he recomposes the result into a finished piece. These days the process mixes photography, illustration and animation with the analog gear.</p><p>The screen matters as much as the circuits. About the colors of a CRT, he said in the same interview: "you just can't emulate this."</p><p>His early portraits show the idea at its most direct. When he shared a series of analog glitch portraits in 2018, he described the work as "traditional portrait art crashing into a hard and corrupted analog circuit-bent imagery."</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/3xfduod59ct1wpg8isdr82vy9nsr/polygon1993-analog-glitch-portrait-2018.jpg" alt="An analog glitch portrait by POLYGON1993 from 2018: a woman's face in deep blue and black, half lost in shadow, with fine horizontal scan lines running across it and a pale pink edge of light along her cheek and neck, as if photographed off a CRT screen." width="1365" height="2048" loading="lazy" srcset="https://pablomurad.com/img/UKnnL6wE-x-480.webp 480w, https://pablomurad.com/img/UKnnL6wE-x-800.webp 800w, https://pablomurad.com/img/UKnnL6wE-x-1200.webp 1200w, https://pablomurad.com/img/UKnnL6wE-x-1365.webp 1365w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="nostalgia-and-what-s-under-it">Nostalgia, and what's under it</h2><p>The obvious theme is nostalgia, but he goes further when he talks about it. In a 2024 <a href="https://blog-v3.opensea.io/articles/get-based-with-polygon1993">interview with OpenSea</a>, he listed the themes he keeps coming back to: nostalgia, depression, loneliness, "but also beauty in the breakdown." In 2021 he put it this way: "I guess I'm chasing this residual image of myself spending countless hours in front a screen when I was a kid."</p><p>That childhood shows everywhere. He grew up with trading cards, video games and MMORPGs, and he still collects Japanese arcade games, trading cards and consoles from the 90s and 2000s. Japanese pop culture runs through the work, from mecha suits to direct nods to Neon Genesis Evangelion and Ghost in the Shell. He also says the colors in his pieces come from the music he listens to while working.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/898hqtlzzc9j0rpkkz688hg0gaey/polygon1993-chrome-portrait.gif" alt="An animated portrait by POLYGON1993: a woman in profile wearing a glossy chrome visor and earpiece, her face and shoulders turning into flowing horizontal lines of white and blue light that ripple and stretch backwards as the image loops, against a black background." width="882" height="1200" loading="lazy"></figure><h2 id="music-brands-and-a-tour-that-stopped">Music, brands and a tour that stopped</h2><p>His work as a scenographer and director has put those visuals on stages and in music videos. He has worked with Tame Impala, Charli XCX, Grimes and A$AP Rocky, and with companies like Bandai Namco, Pantone, Sony Music and Warner Music.</p><p>One of those jobs didn't go as planned. In early 2020 he was working as a content artist on Tame Impala's new live show, and the tour stopped in March because of the pandemic. With the shows on hold, he started selling limited edition holographic prints, a way to bring the work into the physical world, which he says he had always wanted.</p><h2 id="on-chain">On-chain</h2><p>Around the same time, his work found a second home on the blockchain. He has released pieces on Tezos, back in the hic et nunc days, and on Ethereum through platforms like SuperRare and Foundation. In 2024 he dropped The Dance Lesson on Base, and explained to OpenSea that he chose that network for its free mints and "almost non-existent gas fees", which make the work easier to collect. Some of his drops also have game mechanics, like burning one piece to redeem another.</p><p>In 2021, he was already blunt about Instagram's algorithm, which he said was "slowly killing every artist's fire", and he hoped this new space would "take the power back from the algorithms." Instagram, at least, was never very kind to either of us.</p><p>Some of his series:</p><p><strong>azure</strong>, light pulled sideways out of a face, as if the signal were being dragged off the screen:</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/7pnlsag7yx149xq5epm7iajr9lyy/polygon1993-azure.jpg" alt="The cover image of POLYGON1993's azure series: a woman's face drawn in glowing cyan contour lines against a deep blue background, with the lines streaming out of her head to the left like light trails or a signal being pulled sideways." width="800" height="600" loading="lazy" srcset="https://pablomurad.com/img/D0JIh4a6D7-480.webp 480w, https://pablomurad.com/img/D0JIh4a6D7-800.webp 800w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p><strong>Broken Wings</strong>, a small chrome figure under a sky that cracks like glass:</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/8w2e6oh50ughipmk83vnw46jhx88/polygon1993-broken-wings.jpg" alt="The cover image of POLYGON1993's Broken Wings series: a small figure made of shiny chrome and violet light crouches with its head down, hugging its knees on a rippling surface of blue water, while white cracks spread across the black sky behind it like shattered glass." width="500" height="500" loading="lazy" srcset="https://pablomurad.com/img/Guse3zsetB-480.webp 480w, https://pablomurad.com/img/Guse3zsetB-500.webp 500w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p><strong>Panzer Perfume</strong>, where the anime and mecha side of his work comes out, with a pilot dissolving into pixels:</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/uc99swicfkc89mqj44tapdosj0sj/polygon1993-panzer-perfume.jpg" alt="The cover image of POLYGON1993's Panzer Perfume series: an anime-style girl in a glossy lavender mecha suit and helmet with a tall antenna, seen from the side against black, while her body dissolves on the right into a cloud of glittering pixels in green, yellow and pink." width="1400" height="1858" loading="lazy" srcset="https://pablomurad.com/img/M-9Vvb5DDR-480.webp 480w, https://pablomurad.com/img/M-9Vvb5DDR-800.webp 800w, https://pablomurad.com/img/M-9Vvb5DDR-1200.webp 1200w, https://pablomurad.com/img/M-9Vvb5DDR-1400.webp 1400w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>And <strong>First Flight</strong>, a single piece with a figure curled up inside a spiral of light and stars:</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/4jxfsl1mgd3obj61zdevtcko35jc/polygon1993-first-flight.jpg" alt="POLYGON1993's First Flight: a small figure made of striped white and violet light curls up at the center of a spiral of red, magenta and blue streaks, surrounded by bright four-pointed stars in a dark night sky." width="1400" height="1400" loading="lazy" srcset="https://pablomurad.com/img/AfVlQuAk3--480.webp 480w, https://pablomurad.com/img/AfVlQuAk3--800.webp 800w, https://pablomurad.com/img/AfVlQuAk3--1200.webp 1200w, https://pablomurad.com/img/AfVlQuAk3--1400.webp 1400w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="from-the-screen-to-the-room">From the screen to the room</h2><p>The holographic prints were only the beginning. His work now includes canvases, collectible objects, plexiglass sculptures and large installations, and it has been shown in the United States, Japan, France, the United Kingdom, Portugal, Italy and South Korea.</p><p>In 2025 he had a solo show in Tokyo, Paradigm Shifters, at The Harmonyst gallery in Hiroo. And in July 2026 he finally had his first solo exhibition in France, in his own city. Koi No Yokan opened at ArtVerse gallery in Paris, timed with Japan Expo. The title comes from a Japanese expression for the certainty, when you meet someone, that you're going to fall in love with them.</p><p>The show brought together around fifteen works: acrylic sculptures, canvas prints and rooms built around light, with holographic materials borrowed from trading card games. Among them was his first plexiglass sculpture, REI, based on an animated piece he made a few years ago. The highlight was a collaboration with Mitsuhiro Arita, one of the first illustrators of the Pokémon trading card game, whose work had never been shown in a contemporary art gallery before.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/514ygsyvrec7hpo5ps88lxqvsjcy/polygon1993-koi-no-yokan-artverse.jpg" alt="Inside the Koi No Yokan exhibition at ArtVerse gallery in Paris: a white gallery wall under exposed stone holds a row of small, brightly colored framed pieces in blue, orange and yellow, and a man in a cap and light blue T-shirt stands close to the wall, looking at the works." width="1200" height="760" loading="lazy" srcset="https://pablomurad.com/img/eCgOi4A97z-480.webp 480w, https://pablomurad.com/img/eCgOi4A97z-800.webp 800w, https://pablomurad.com/img/eCgOi4A97z-1200.webp 1200w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="where-to-follow-him">Where to follow him</h2><p>His main channel, he told OpenSea, is X, where he's <a href="https://x.com/Polygon1993">@Polygon1993</a>. His series are collected on <a href="https://verse.works/polygon1993">Verse</a>, and he also has pages on <a href="https://superrare.com/polygon1993">SuperRare</a> and <a href="https://objkt.com/@polygon1993">objkt</a>, plus a <a href="https://polygon1993.tumblr.com/">Tumblr</a>.</p><p>On Instagram he's <a href="https://www.instagram.com/polygon1993/">@polygon1993</a>. Try not to get banned.</p>]]></content>
    </entry>
    <entry>
        <title>The IndieWeb and the small web, explained</title>
        <link href="https://pablomurad.com/indieweb-and-small-web-explained/"/>
        <id>https://pablomurad.com/indieweb-and-small-web-explained/</id>
        <published>2026-11-10T15:27:05-03:00</published>
        <updated>2026-11-10T15:27:05-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="indieweb"/>
        <summary>What the IndieWeb and the small web are, how they differ and where they meet: POSSE, microformats, Webmention and IndieAuth on one side, Neocities, webrings, Gemini and pubnixes on the other. Plus where to find these sites and how to start your own.</summary>
        <content type="html"><![CDATA[<p>For most people today, the web is a few apps. You open one, scroll, and whatever you post lives on someone else's servers, under rules you didn't write, inside a feed you don't control.</p><p>Underneath all of that, the original web is still there and still works the same way. Anyone can get a domain, put an HTML file on a server, and anyone else in the world can read it with a browser, no account needed. A lot of people never stopped doing that. Two names keep coming up when people talk about them: the IndieWeb and the small web. They overlap a lot, and they aren't the same thing, so here's what each one means, what it looks like in practice and how to get in.</p><h2 id="the-indieweb">The IndieWeb</h2><p>The IndieWeb describes itself as "a people-focused alternative to the corporate web": a community of independent and personal websites built around a few ideas. You own your domain and use it as your main identity online. You publish on your own site first, and only then, if you want, somewhere else. And you own what you write.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/uqrfd2vbaxpujzrbf6rsh0krytzc/indieweb-org.png" alt="The home page of indieweb.org, with the red and orange IndieWebCamp logo at the top. A grey box asks &quot;What is the IndieWeb?&quot; and answers that it is a people-focused alternative to the &quot;corporate web&quot;, a community of independent and personal websites based on owning your domain, publishing on your own site first and owning your content. Below, three yellow icons introduce the sections &quot;Your content is yours&quot;, &quot;You are better connected&quot; and &quot;You are in control&quot;." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/CzsjoOLKWG-480.webp 480w, https://pablomurad.com/img/CzsjoOLKWG-800.webp 800w, https://pablomurad.com/img/CzsjoOLKWG-1200.webp 1200w, https://pablomurad.com/img/CzsjoOLKWG-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>It has a home at <a href="https://indieweb.org">indieweb.org</a>, a wiki that is also its documentation, and a history of meetups. The earliest IndieWebCamp on the wiki's list happened in Portland, Oregon, in 2011, and there are still small online and local gatherings for people who are building their own sites.</p><p>The community keeps a list of <a href="https://indieweb.org/principles">principles</a>. Some are what you'd expect: own your domain and data, use what you make, make your code open source, build for the long web. Others are a little more surprising, like "Make what you need" (for yourself first, not for "everyone") and "Pluralism", which means there's no single right way or software to do any of this. The last one closes the list: "Above all, Have fun. Keep the web weird and interesting."</p><h3 id="posse">POSSE</h3><p>The idea people bring up most is POSSE: Publish (on your) Own Site, Syndicate Elsewhere. You write the post on your site, at your address, and then share a copy or a link on Mastodon, Bluesky or wherever your friends are. If the platform disappears, changes its rules or locks you out, the original is still at your address. The links you shared over the years keep working, because they point to you.</p><h3 id="the-building-blocks">The building blocks</h3><p>Owning a site is the easy part. The interesting work is making independent sites talk to each other the way social networks do, with replies, likes and follows, but without a company in the middle. The IndieWeb uses a handful of small standards for that, and none of them needs more than ordinary HTML and HTTP:</p><ul>
<li value="1">
<strong>Microformats</strong> are plain class names you add to your HTML so software can read it. An <code>h-card</code> says who you are (name, photo, URL), an <code>h-entry</code> marks a post, with its title, date and content. Your page looks exactly the same to people.</li>
<li value="2">
<strong>rel="me"</strong> links connect your site to your other profiles. If your site links to your GitHub with <code>rel="me"</code> and your GitHub links back to your site, that's proof both belong to the same person. Mastodon uses the same trick to show a verified link on your profile.</li>
<li value="3">
<strong>Webmention</strong> is how sites notify each other. When you write a reply on your blog to a post on someone else's blog, your site sends a small notice to theirs saying "this page mentions you". Their site can then show your reply as a comment. It became a W3C Recommendation in January 2017.</li>
<li value="4">
<strong>IndieAuth</strong> lets you sign in to services using your own domain as your identity, instead of a Google or Facebook account.</li>
<li value="5">
<strong>Micropub</strong> lets you publish to your site from other apps, like a phone app for quick notes or photos. It became a W3C Recommendation in May 2017.</li>
</ul><p>You don't need all of them. Most IndieWeb sites start with a domain, an h-card and a few rel="me" links, and add the rest over time.</p><h3 id="what-that-looks-like-on-this-blog">What that looks like on this blog</h3><p>This blog runs on PaCord, my own CMS, and it speaks most of that language. If you open the source of the home page, among the usual tags in the <code>&lt;head&gt;</code>, there are lines like these:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>&lt;link rel="webmention" href="https://cms.pablomurad.com/webmention"&gt;</span></span>
<span class="line"><span>&lt;link rel="micropub" href="https://cms.pablomurad.com/micropub"&gt;</span></span>
<span class="line"><span>&lt;link rel="authorization_endpoint" href="https://cms.pablomurad.com/indieauth/auth"&gt;</span></span>
<span class="line"><span>&lt;link rel="token_endpoint" href="https://cms.pablomurad.com/indieauth/token"&gt;</span></span></code></pre></div><p>Those tell other software where to send webmentions, where a Micropub app can post, and how to sign in with pablomurad.com through IndieAuth. Further down, the page has an h-card with my name and photo, every post is marked as an h-entry, and the footer links to my other profiles with <code>rel="me"</code>: GitHub, Codeberg, sourcehut, my own Git server and murad.social, among others. There are feeds too, in RSS, Atom, JSON Feed and JF2, so any reader can follow along.</p><p>The easiest way to check your own site is <a href="https://indiewebify.me">IndieWebify.me</a>, which walks you through each step and tests your pages. This is what it finds here:</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/s7w5pz1rrw76x6zxj35oxn6evg9i/indiewebify-hcard-pablomurad.png" alt="The IndieWebify.me page &quot;Publishing on the IndieWeb, Level 2&quot;, step 1, &quot;Mark up your content with microformats2&quot;. The address https://pablomurad.com/ is in the input box next to a green &quot;Validate h-card&quot; button. Below, a green &quot;Success!&quot; box says a representative h-card was found, showing a photo of Pablo Murad, his name, the URL https://pablomurad.com, an email link, the note &quot;Technology, education and freedom.&quot; and the nickname runawaydevil." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/Z0DV5hCN7J-480.webp 480w, https://pablomurad.com/img/Z0DV5hCN7J-800.webp 800w, https://pablomurad.com/img/Z0DV5hCN7J-1200.webp 1200w, https://pablomurad.com/img/Z0DV5hCN7J-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="the-small-web">The small web</h2><p>The small web is harder to pin down, because nobody owns the term. There's no wiki that defines it, no list of principles and no standard to implement. It's a loose name for the part of the internet made of personal, non-commercial, usually lightweight sites, made by one person or a few, because they wanted that thing to exist.</p><p>Where the IndieWeb talks about protocols and ownership, the small web is about scale and feel. A small web site is usually:</p><ul>
<li value="1">personal: a homepage, a blog, a fan shrine, a collection of recipes, a page about one strange hobby;</li>
<li value="2">light: plain HTML, a little CSS, few or no scripts, no trackers and no cookie banners;</li>
<li value="3">made by hand, or close to it, often with the seams showing;</li>
<li value="4">connected in old ways: links to friends, webrings, blogrolls, guestbooks, RSS, and those tiny 88x31 buttons from the 2000s.</li>
</ul><p>Some people take the "small" literally. The <a href="https://512kb.club">512KB Club</a> and the 1MB Club, which showed up in Neat Finds #02, only list sites whose pages weigh less than that.</p><p>The small web also goes beyond HTTP. Gemini is a protocol made in 2019 to be deliberately simpler than the web, with text pages and no scripts at all. Gopher, from 1991, still has people writing phlogs on it. Nex is even smaller: you connect, send a path and get back text. And there are pubnixes, or tildes, shared Unix servers where members get a shell account, a home page at <code>/~name</code> and a community that talks through the terminal.</p><h3 id="where-it-lives">Where it lives</h3><p>A lot of the small web lives on <a href="https://neocities.org">Neocities</a>, a free host for static sites that took the place GeoCities left empty. You get <code>yourname.neocities.org</code>, an editor in the browser and a community where sites follow each other. The counter on its home page shows more than 1.8 million sites.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/wxsmjha9hqnmef16chtasv0bbnjx/neocities.png" alt="The Neocities home page, teal and dark grey, with a cartoon cat holding a paintbrush next to the word &quot;neocities&quot; and line drawings of houses and stars. The headline reads &quot;Create your own free website. Unlimited creativity, zero ads.&quot; and the text below mentions a network of 1,828,000 web sites. On the right, a &quot;Sign up for free&quot; form asks for a username ending in .neocities.org, tags, password and email." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/y6-7_TOi57-480.webp 480w, https://pablomurad.com/img/y6-7_TOi57-800.webp 800w, https://pablomurad.com/img/y6-7_TOi57-1200.webp 1200w, https://pablomurad.com/img/y6-7_TOi57-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The rest is spread across personal domains, cheap shared hosting, home servers, Raspberry Pis on a shelf, pubnixes and Gemini capsules.</p><h3 id="how-to-find-it">How to find it</h3><p>You won't find much of it through the big search engines or social feeds, which favor large commercial sites. A few tools are made for this.</p><p><a href="https://kagi.com/smallweb">Kagi Small Web</a> shows you recent posts from personal blogs, independent video channels and webcomics, one at a time. You press "Next Post" and land on someone's site, with a thin toolbar on top. It's free to use, and the list of sources is open on GitHub.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/mq66vj1tfhun50ysqhixhcyupmnp/kagi-small-web.png" alt="Kagi Small Web open in a browser. A dark toolbar at the top has a &quot;Next Post&quot; button, a heart, a search box and tabs for Blogs, Liked, Videos, Code, Comics and River, plus Topics, Share, Similar and Flag. Below it, a personal site is shown in full: the header of an artist's blog and a post titled &quot;The Censor Within&quot;, dated November 7, 2026." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/5jX4hxLzYm-480.webp 480w, https://pablomurad.com/img/5jX4hxLzYm-800.webp 800w, https://pablomurad.com/img/5jX4hxLzYm-1200.webp 1200w, https://pablomurad.com/img/5jX4hxLzYm-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p><a href="https://marginalia-search.com">Marginalia Search</a> is an independent search engine that puts non-commercial, text-heavy sites first and helps you find old websites the big engines forgot. It's open source, and it doesn't share your data or keep your IP address for long.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/7tqva3vrzdj5yk4p1vofctkeck0m/marginalia-search.png" alt="The Marginalia Search home page: a plain search box with a &quot;Search&quot; button, and three boxes below. &quot;Explore the Web&quot; says it prioritizes non-commercial content, has tools for search and discovery and finds lost old websites. &quot;Open Source&quot; lists custom index and crawler software, simple technology with no AI, and the AGPL license. &quot;Privacy by default&quot; says it filters out tracking, shares no data with third parties and keeps no long-term record of IPs." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/GZdrRevrD2-480.webp 480w, https://pablomurad.com/img/GZdrRevrD2-800.webp 800w, https://pablomurad.com/img/GZdrRevrD2-1200.webp 1200w, https://pablomurad.com/img/GZdrRevrD2-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Then there are the old ways, which still work. Webrings, where every site links to the next one in a loop. Directories made by people instead of algorithms. And the links page on the sites you already like: one personal site almost always leads to five more.</p><h2 id="same-neighborhood-different-streets">Same neighborhood, different streets</h2><p>A Neocities shrine to a 90s cartoon, with a guestbook and a hit counter, is pure small web, and it doesn't need a single microformat. A developer's blog with Webmention, IndieAuth and a Micropub app is IndieWeb, even if it's built on a big framework and isn't small at all. Many sites are both, and nobody is checking.</p><p>What they share is the starting point: a person with their own address, publishing for other people, with no platform deciding who gets to see it.</p><h2 id="my-corners-of-it">My corners of it</h2><p>Besides this blog, a few of my projects live in this neighborhood.</p><p><a href="https://weirdnet.org">weirdnet</a> is a directory of the small and personal web, organized by hand. It has more than 50,000 links across 314 categories, from personal sites and archives to webrings, amateur radio and the 512KB Club, plus a random button that sends you somewhere unexpected. If you know a site that belongs there, the suggestion page explains how to send it. There's also a short guide on <a href="https://weirdnet.org/indieweb/">starting your own site</a>.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/1rycq15bp0nl1v9w4i5ef7vyebvm/weirdnet.png" alt="The weirdnet.org home page on a white background, with the word &quot;weirdnet&quot; in red and yellow at the top and grey buttons for Directory, Link Dump, What's New, Search, Random and Info. Under the line &quot;A hand-made map of the small, weird, personal web. 50,000+ assorted links&quot;, a green counter reads 050986. Below, blue category links in two columns: Personal sites (9,443), Archives (4,819), Programming, Visual arts, Web design, Games, Literature, Music scenes, Retro web, 512kb club, Fun, Music, Amateur radio and Net art, each with a few example sites." width="1280" height="800" loading="lazy" srcset="https://pablomurad.com/img/u31IJbDw-q-480.webp 480w, https://pablomurad.com/img/u31IJbDw-q-800.webp 800w, https://pablomurad.com/img/u31IJbDw-q-1200.webp 1200w, https://pablomurad.com/img/u31IJbDw-q-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p><a href="https://phlog.space">phlog.space</a> is a small, free photo log, written in plain PHP with SQLite and no JavaScript. <a href="https://soledade.city">Soledade</a> is a blog in Portuguese shaped like a little town, which you can also read from a terminal over a Nex-style port. <a href="https://runv.club">runv.club</a> is a pubnix, and noctem.cafe and noctem9.cafe are two more I look after. And if you want to build a site by hand, <a href="https://pablo.how">Pablo, how?</a> has a seven-part series called Web by hand that goes from an empty folder to a styled site with its own RSS feed, and <a href="https://docs.pablomurad.com">Docs do Murad</a> has the same kind of material in Portuguese.</p><h2 id="how-to-start">How to start</h2><p>You don't need permission or a framework. A reasonable path:</p><ol>
<li value="1">Get a place to live. A domain of your own is the IndieWeb way, and it keeps your address yours forever. A free Neocities page is the fastest way to start today, and you can point a domain at it later.</li>
<li value="2">Write one page by hand: who you are, what you like, a few links. It can be ugly. Plenty of sites start like that.</li>
<li value="3">Add an h-card to that page and <code>rel="me"</code> links to your other profiles. Ten minutes of work, and IndieWebify.me will tell you if it's right.</li>
<li value="4">Start a feed, even if you only post once a month. RSS is still how most people on the small web follow each other.</li>
<li value="5">Go out and meet the neighbors. Join a webring, sign a few guestbooks, add a links page, and send your site to directories.</li>
</ol><p>From there, add whatever you feel like: webmentions, a guestbook, a Gemini mirror, an 88x31 button. Your site will change over the years, and it will still be at the same address.</p>]]></content>
    </entry>
    <entry>
        <title>A practical guide to WebFinger</title>
        <link href="https://pablomurad.com/practical-guide-to-webfinger/"/>
        <id>https://pablomurad.com/practical-guide-to-webfinger/</id>
        <published>2026-11-10T14:03:41-03:00</published>
        <updated>2026-11-10T14:03:41-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="webfinger"/>
        <summary>WebFinger is the small protocol that lets a fediverse server find @you@yourdomain. What it is, how a request and its JSON answer look, who uses it, and three ways to set it up on your own domain: a static file, an nginx map and a short PHP script that follows RFC 7033.</summary>
        <content type="html"><![CDATA[<p>Type <code>@pablo@pablomurad.com</code> into the search box of any Mastodon server and you'll find an account. That account doesn't live on this blog. It lives on murad.social, a GoToSocial server, under the handle <code>@pablo@murad.social</code>. What connects the two is a single HTTPS request to this site, at an address that starts with <code>/.well-known/webfinger</code>, and a few lines of JSON that come back.</p><p>That protocol is WebFinger. It's small, it has been around since 2013, and a lot of the fediverse depends on it. This post explains what it is, how a request and an answer look, who uses it, and how to set it up on your own domain, from a single static file to a short script that follows the spec properly.</p><h2 id="where-the-name-comes-from">Where the name comes from</h2><p>Long before the web, Unix systems had <code>finger</code>. You typed <code>finger user@host</code> and the other machine told you who that person was, when they last logged in, and whatever they wrote in their <code>.plan</code> file. The first spec, RFC 742, is from 1977. (Finger.Farm, from Neat Finds #04, still hosts finger accounts today.)</p><p>WebFinger takes the same idea to HTTPS. You ask a domain about an identifier, usually something that looks like an email address, and the domain answers with a JSON document full of links: where the profile is, where the avatar is, which server handles the account. It's <a href="https://www.rfc-editor.org/info/rfc7033/">RFC 7033</a>, published in September 2013 as a Proposed Standard. The project's home is <a href="https://webfinger.net/">webfinger.net</a>, with the spec, a list of code and the link relations it registers.</p><h2 id="one-request-one-answer">One request, one answer</h2><p>Every WebFinger request goes to the same path on the domain, <code>/.well-known/webfinger</code>, with a <code>resource</code> parameter saying who or what you're asking about. You can try it with curl:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>curl -s "https://pablomurad.com/.well-known/webfinger?resource=acct:pablo@pablomurad.com"</span></span></code></pre></div><p>This is what comes back (formatted here, one item per line):</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>{</span></span>
<span class="line"><span>"subject": "acct:pablo@murad.social",</span></span>
<span class="line"><span>"aliases": [</span></span>
<span class="line"><span>"https://murad.social/users/pablo",</span></span>
<span class="line"><span>"https://murad.social/@pablo"</span></span>
<span class="line"><span>],</span></span>
<span class="line"><span>"links": [</span></span>
<span class="line"><span>{</span></span>
<span class="line"><span>"rel": "http://webfinger.net/rel/profile-page",</span></span>
<span class="line"><span>"type": "text/html",</span></span>
<span class="line"><span>"href": "https://murad.social/@pablo"</span></span>
<span class="line"><span>},</span></span>
<span class="line"><span>{</span></span>
<span class="line"><span>"rel": "self",</span></span>
<span class="line"><span>"type": "application/activity+json",</span></span>
<span class="line"><span>"href": "https://murad.social/users/pablo"</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span>]</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>A few things to notice in the request. <code>acct:</code> is a URI scheme made for accounts, defined later in RFC 7565. It looks like an email address, but nothing says there has to be a mailbox behind it. The resource can also be any other URI, like <code>https://pablomurad.com/</code> or a <code>mailto:</code> address, as long as the server knows what to do with it.</p><p>The answer is a JRD, short for JSON Resource Descriptor, and it's sent with the content type <code>application/jrd+json</code>. It has four possible members:</p><ul>
<li value="1">
<code>subject</code> is the URI the document describes. The RFC says it SHOULD be there.</li>
<li value="2">
<code>aliases</code> is a list of other URIs for the same thing.</li>
<li value="3">
<code>properties</code> holds extra facts about the subject, as name and value pairs where each name is a URI.</li>
<li value="4">
<code>links</code> is where most of the work happens. Each link has a <code>rel</code> (the only required field, saying what kind of link it is), and optionally a <code>type</code>, an <code>href</code>, <code>titles</code> in different languages and its own <code>properties</code>.</li>
</ul><p>Look at the subject in the answer above: the question was about <code>acct:pablo@pablomurad.com</code> and the answer is about <code>acct:pablo@murad.social</code>. That's allowed, and it's the reason the trick works. Mastodon's docs explain what happens next: when the subject differs from what was asked, Mastodon makes a second WebFinger request to the domain in the subject, checks that it points to the same account, and goes with that.</p><h2 id="the-rules-in-the-spec">The rules in the spec</h2><p>RFC 7033 is short, and the rules a server has to follow fit in a list:</p><ul>
<li value="1">HTTPS only. Clients must query over HTTPS, and the resource must not be served over plain HTTP. Redirects are allowed, but only to another <code>https</code> address.</li>
<li value="2">The query must have a <code>resource</code> parameter. If it's missing or malformed, the answer is 400 Bad Request.</li>
<li value="3">If the server doesn't know the resource, the answer is 404.</li>
<li value="4">Responses must carry the <code>Access-Control-Allow-Origin</code> header, so code running in a browser can read them. In practice that's <code>Access-Control-Allow-Origin: *</code>.</li>
<li value="5">Servers should support the <code>rel</code> parameter. A client can send one or more <code>rel</code> values, and the server returns only the links that match. The subject, aliases and properties come back either way.</li>
<li value="6">Clients must ignore any member of the JRD they don't understand. That's what lets you add your own fields without breaking anyone.</li>
</ul><h2 id="who-asks-these-questions">Who asks these questions</h2><p>The fediverse is the biggest user. When you search for <code>@someone@example.com</code> on Mastodon, GoToSocial or most other servers, the first thing they do is ask <code>example.com</code> over WebFinger. Mastodon's documentation lists the links it cares about:</p><ul>
<li value="1">
<code>self</code>, with type <code>application/activity+json</code>, pointing to the ActivityPub actor. This is the link Mastodon really needs. A minimal valid answer for Mastodon is a subject plus this one link.</li>
<li value="2">
<code>http://webfinger.net/rel/profile-page</code>, the HTML profile a person would open in a browser.</li>
<li value="3">
<code>http://ostatus.org/schema/1.0/subscribe</code>, with a <code>template</code> field instead of <code>href</code>. Other servers use it to send you to your own instance to follow someone.</li>
</ul><p>OpenID Connect uses WebFinger to find the identity provider for an address. The link has <code>rel</code> set to <code>http://openid.net/specs/connect/1.0/issuer</code> and its <code>href</code> is the issuer URL. The RFC's own first example is exactly this, and services use it in the wild: Tailscale, when you sign up with a custom OIDC provider, asks the domain of your email address over WebFinger and expects that issuer link back, matching the issuer in your provider's <code>/.well-known/openid-configuration</code>.</p><p>webfinger.net also keeps a small list of its own link relations. There are two: <code>http://webfinger.net/rel/avatar</code> for a picture and <code>http://webfinger.net/rel/profile-page</code> for the main page about the account.</p><h2 id="setting-it-up">Setting it up</h2><p>There are three ways to do this, from quickest to most correct. Pick the one that fits how many people live on your domain.</p><p>One note on the code below: it's written flat, with no indentation, because this blog's editor collapses repeated spaces. nginx, Caddy and PHP don't care.</p><h3 id="one-static-file">One static file</h3><p>This is the fastest way to make <code>you@yourdomain</code> find your fediverse account. Start by saving the answer your own server already gives (swap in your handle and instance):</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>curl -s "https://murad.social/.well-known/webfinger?resource=acct:pablo@murad.social" &gt; webfinger.json</span></span></code></pre></div><p>Put <code>webfinger.json</code> somewhere on your web server and tell the server to answer the WebFinger path with it. On nginx, inside the <code>server</code> block of your domain:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>location = /.well-known/webfinger {</span></span>
<span class="line"><span>types { }</span></span>
<span class="line"><span>default_type application/jrd+json;</span></span>
<span class="line"><span>add_header Access-Control-Allow-Origin "*" always;</span></span>
<span class="line"><span>alias /var/www/example.com/webfinger.json;</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>The empty <code>types { }</code> is there on purpose. Without it, nginx looks at the <code>.json</code> extension and sends <code>application/json</code>, which isn't the content type the spec asks for.</p><p>On Caddy, inside your site block:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>handle /.well-known/webfinger {</span></span>
<span class="line"><span>header Content-Type application/jrd+json</span></span>
<span class="line"><span>header Access-Control-Allow-Origin *</span></span>
<span class="line"><span>rewrite * /webfinger.json</span></span>
<span class="line"><span>file_server</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>This assumes your site block already has a <code>root</code> pointing to the folder where the file lives.</p><p>Reload the server and test it. Then go to any fediverse account and search for <code>@you@yourdomain</code>. Your profile should show up.</p><p>There are trade-offs, and they all come from the file not reading the question:</p><ul>
<li value="1">Every question gets the same answer. Ask about <code>anyone@yourdomain</code> and you get your account. Ask with no <code>resource</code> at all and you still get a 200 instead of the 400 the spec asks for.</li>
<li value="2">The <code>rel</code> filter is ignored, so clients always get every link.</li>
<li value="3">Your domain becomes a pointer, and your handle doesn't change. People can find you as <code>@pablo@pablomurad.com</code>, but your posts still show <code>@pablo@murad.social</code>, because that's the subject in the answer.</li>
</ul><p>pablomurad.com answers exactly like this today: any name at this domain leads to the same account. For a domain with one person behind it, that's usually fine.</p><h3 id="several-accounts-with-an-nginx-map">Several accounts with an nginx map</h3><p>If a few people share a domain, nginx can still do it without any code. A <code>map</code> picks a file based on the <code>resource</code> parameter. The <code>map</code> goes in the <code>http</code> block, outside any <code>server</code>:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>map $arg_resource $webfinger_file {</span></span>
<span class="line"><span>default "";</span></span>
<span class="line"><span>"acct:pablo@example.com" pablo.json;</span></span>
<span class="line"><span>"acct%3Apablo%40example.com" pablo.json;</span></span>
<span class="line"><span>"acct:ana@example.com" ana.json;</span></span>
<span class="line"><span>"acct%3Aana%40example.com" ana.json;</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>And the location goes inside your <code>server</code>:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>location = /.well-known/webfinger {</span></span>
<span class="line"><span>if ($arg_resource = "") { return 400; }</span></span>
<span class="line"><span>if ($webfinger_file = "") { return 404; }</span></span>
<span class="line"><span>types { }</span></span>
<span class="line"><span>default_type application/jrd+json;</span></span>
<span class="line"><span>add_header Access-Control-Allow-Origin "*" always;</span></span>
<span class="line"><span>root /var/www/webfinger;</span></span>
<span class="line"><span>try_files /$webfinger_file =404;</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>Each account gets its own file in <code>/var/www/webfinger</code>. Now a missing resource gets a 400, an unknown name gets a 404, and each person gets their own answer.</p><p>Each account appears twice in the map because <code>$arg_resource</code> is the raw query string. Some clients send <code>acct:pablo@example.com</code> and others encode it as <code>acct%3Apablo%40example.com</code>, and nginx doesn't decode it for you. The map is also case-sensitive. It's fine for a handful of accounts. Past that, or if you want the <code>rel</code> filter, it's time for a script.</p><h3 id="a-short-php-script-that-follows-the-spec">A short PHP script that follows the spec</h3><p>PHP runs almost everywhere, from a VPS to cheap shared hosting, and it's enough for this. The script reads a list of accounts, each pointing to a JSON file, and handles everything the spec asks for: the 400, the 404, CORS, the right content type and the <code>rel</code> filter.</p><p>Save it as <code>webfinger.php</code> at the root of your site:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>&lt;?php</span></span>
<span class="line"><span>// webfinger.php: answers /.well-known/webfinger for the accounts listed below.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Every resource you want to answer for, pointing to its JSON file.</span></span>
<span class="line"><span>$accounts = [</span></span>
<span class="line"><span>'acct:pablo@example.com' =&gt; __DIR__ . '/webfinger/pablo.json',</span></span>
<span class="line"><span>'https://example.com/' =&gt; __DIR__ . '/webfinger/pablo.json',</span></span>
<span class="line"><span>];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Any web page may read this, so CORS is open (RFC 7033 asks for it).</span></span>
<span class="line"><span>header('Access-Control-Allow-Origin: *');</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Read the query by hand: PHP keeps only the last "rel" if there are several.</span></span>
<span class="line"><span>$resource = '';</span></span>
<span class="line"><span>$rels = [];</span></span>
<span class="line"><span>foreach (explode('&amp;', $_SERVER['QUERY_STRING'] ?? '') as $pair) {</span></span>
<span class="line"><span>[$key, $value] = array_pad(explode('=', $pair, 2), 2, '');</span></span>
<span class="line"><span>$value = urldecode($value);</span></span>
<span class="line"><span>if ($key === 'resource') { $resource = $value; }</span></span>
<span class="line"><span>if ($key === 'rel') { $rels[] = $value; }</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// No resource at all: a bad request.</span></span>
<span class="line"><span>if ($resource === '') {</span></span>
<span class="line"><span>http_response_code(400);</span></span>
<span class="line"><span>exit("Missing resource parameter\n");</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Nobody by that name here.</span></span>
<span class="line"><span>if (!isset($accounts[$resource])) {</span></span>
<span class="line"><span>http_response_code(404);</span></span>
<span class="line"><span>exit("Not found\n");</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>$jrd = json_decode(file_get_contents($accounts[$resource]), true);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// If the client asked for specific rels, return only those links.</span></span>
<span class="line"><span>if ($rels &amp;&amp; isset($jrd['links'])) {</span></span>
<span class="line"><span>$jrd['links'] = array_values(array_filter(</span></span>
<span class="line"><span>$jrd['links'],</span></span>
<span class="line"><span>fn($link) =&gt; in_array($link['rel'] ?? '', $rels, true)</span></span>
<span class="line"><span>));</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>header('Content-Type: application/jrd+json');</span></span>
<span class="line"><span>echo json_encode($jrd, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), "\n";</span></span></code></pre></div><p>The JSON files go in a <code>webfinger/</code> folder next to it, one per person, written by hand or copied from your fediverse server like before. The second line in <code>$accounts</code> shows that a resource doesn't have to be an <code>acct:</code>. Here, asking about the homepage leads to the same person.</p><p>The part people trip on is the query string. PHP builds <code>$_GET</code> with the last value of each name, so <code>rel=self&amp;rel=avatar</code> would turn into just <code>avatar</code>. Parsing <code>QUERY_STRING</code> by hand keeps both. The arrow function needs PHP 7.4 or newer.</p><p>Now point the WebFinger path at the script. On nginx with PHP-FPM:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>location = /.well-known/webfinger {</span></span>
<span class="line"><span>include fastcgi_params;</span></span>
<span class="line"><span>fastcgi_param SCRIPT_FILENAME /var/www/example.com/webfinger.php;</span></span>
<span class="line"><span>fastcgi_pass unix:/run/php/php8.3-fpm.sock;</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>The socket name changes with the PHP version, so check yours with <code>ls /run/php/</code>.</p><p>On Caddy, it's a rewrite plus the PHP handler. The query string survives the rewrite:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>example.com {</span></span>
<span class="line"><span>root * /var/www/example.com</span></span>
<span class="line"><span>rewrite /.well-known/webfinger /webfinger.php</span></span>
<span class="line"><span>php_fastcgi unix//run/php/php8.3-fpm.sock</span></span>
<span class="line"><span>file_server</span></span>
<span class="line"><span>}</span></span></code></pre></div><h2 id="when-you-want-the-handle-itself-on-your-domain">When you want the handle itself on your domain</h2><p>Everything above makes your domain point to an account that lives somewhere else. If you run your own Mastodon or GoToSocial server and want the handle to be <code>@you@yourdomain</code> for real, while the server runs on a subdomain like <code>social.yourdomain</code>, both have a setting for it:</p><ul>
<li value="1">Mastodon calls it <code>LOCAL_DOMAIN</code> (the domain in the handles) and <code>WEB_DOMAIN</code> (where the server actually runs).</li>
<li value="2">GoToSocial calls it <code>account-domain</code> and <code>host</code>.</li>
</ul><p>In both cases the main domain has to redirect WebFinger to the server. GoToSocial also wants <code>/.well-known/host-meta</code> and <code>/.well-known/nodeinfo</code> redirected. This is the Caddy version from the GoToSocial docs, on the main domain:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>example.com {</span></span>
<span class="line"><span>redir /.well-known/host-meta* https://social.example.com{uri} permanent</span></span>
<span class="line"><span>redir /.well-known/webfinger* https://social.example.com{uri} permanent</span></span>
<span class="line"><span>redir /.well-known/nodeinfo* https://social.example.com{uri} permanent</span></span>
<span class="line"><span>}</span></span></code></pre></div><p>Mastodon's docs add that this redirect needs CORS headers too, or parts of the web interface break.</p><p>This has to be decided before the first start. Both projects say the same thing in their docs: once your server has federated with others, the domain layout can't be safely changed, because remote servers cache the mapping and get confused. If you already have an account somewhere and only want a nicer address, the static file is the safer path.</p><h2 id="making-it-yours">Making it yours</h2><p>Since clients ignore what they don't understand, the JRD is a good place to collect your links. Some ideas:</p><ul>
<li value="1">An avatar, with <code>rel</code> set to <code>http://webfinger.net/rel/avatar</code> and the image's <code>type</code>.</li>
<li value="2">A profile page pointing to your blog instead of your fediverse profile.</li>
<li value="3">
<code>titles</code>, so a link can carry a name in more than one language, like <code>{"en": "My blog", "pt-BR": "Meu blog"}</code>.</li>
<li value="4">An OpenID Connect issuer link, if you run your own identity provider. Pocket ID, from the first Neat Finds, is one.</li>
<li value="5">Your own link types. A <code>rel</code> can be any URI, so you can make one up under your domain, like <code>https://example.com/rel/feed</code> with <code>type</code> set to <code>application/atom+xml</code> for your blog's feed.</li>
<li value="6">
<code>properties</code>, also named by URIs, for small facts like your display name.</li>
</ul><p>For a full example, look at packetizer.com, home of one of the RFC's authors. His address there returns properties with his name in English and Chinese and the date the account was created, plus links to an avatar, a profile page, his blog with a title, a vCard, his blog's Atom feed and an OpenID provider:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>curl -s "https://packetizer.com/.well-known/webfinger?resource=acct:paulej@packetizer.com"</span></span></code></pre></div><p>Here's what a personal JRD with a few of those ideas could look like. The <code>example.com</code> names are placeholders:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>{</span></span>
<span class="line"><span>"subject": "acct:pablo@example.com",</span></span>
<span class="line"><span>"aliases": ["https://example.com/"],</span></span>
<span class="line"><span>"properties": {</span></span>
<span class="line"><span>"https://example.com/ns/name": "Pablo"</span></span>
<span class="line"><span>},</span></span>
<span class="line"><span>"links": [</span></span>
<span class="line"><span>{"rel": "self", "type": "application/activity+json", "href": "https://social.example.net/users/pablo"},</span></span>
<span class="line"><span>{"rel": "http://webfinger.net/rel/profile-page", "type": "text/html", "href": "https://example.com/"},</span></span>
<span class="line"><span>{"rel": "http://webfinger.net/rel/avatar", "type": "image/png", "href": "https://example.com/avatar.png"},</span></span>
<span class="line"><span>{"rel": "https://example.com/rel/feed", "type": "application/atom+xml", "href": "https://example.com/feed.xml", "titles": {"en": "Blog", "pt-BR": "Blog"}}</span></span>
<span class="line"><span>]</span></span>
<span class="line"><span>}</span></span></code></pre></div><h2 id="testing-it">Testing it</h2><p>curl with <code>-i</code> shows the headers, which is where most mistakes hide:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>curl -si "https://example.com/.well-known/webfinger?resource=acct:pablo@example.com"</span></span></code></pre></div><p>Check the status (200), <code>Content-Type: application/jrd+json</code> and <code>Access-Control-Allow-Origin: *</code>. Then try the edge cases:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/.well-known/webfinger"</span></span>
<span class="line"><span>curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/.well-known/webfinger?resource=acct:nobody@example.com"</span></span>
<span class="line"><span>curl -s "https://example.com/.well-known/webfinger?resource=acct:pablo@example.com&amp;rel=self" | jq ".links[].rel"</span></span></code></pre></div><p>The first should print 400, the second 404, and the third only <code>"self"</code>. With the static file, all three give you the full answer, as described above. The final test is the human one: search for the address from a fediverse account and see if your profile shows up.</p><h2 id="what-you-re-publishing">What you're publishing</h2><p>Everything in that JSON is public, and the open CORS header means any web page can read it with a line of JavaScript. RFC 7033 is explicit about this: WebFinger must not be used to publish personal data unless the person explicitly agreed, and a service that publishes data about its users must let them choose what goes out. If you're answering for other people, ask them what they want in their file.</p><p>Keep phone numbers and private email addresses out of it unless you want them collected. And if you run a server for many accounts, the RFC recommends taking steps against abuse, since a 404 for unknown names and a 200 for real ones is enough to check, one request at a time, who has an account.</p>]]></content>
    </entry>
    <entry>
        <title>Neat Finds #05: Homepages, Pocket Synths and a Server in a QR Code</title>
        <link href="https://pablomurad.com/neat-finds-05-homepages-pocket-synths-and-a-server-in-a-qr-code/"/>
        <id>https://pablomurad.com/neat-finds-05-homepages-pocket-synths-and-a-server-in-a-qr-code/</id>
        <published>2026-11-09T15:51:41-03:00</published>
        <updated>2026-11-09T15:51:42-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="neat-finds"/>
        <summary>Issue five. Most of this batch is about places where people put their own words: directories of personal sites, a homepage host, a couple of blogging platforms, a forum, a guestbook, and servers for protocols most people have never heard of. Toward the end it gets stranger, with an NES</summary>
        <content type="html"><![CDATA[<p>Issue five. Most of this batch is about places where people put their own words: directories of personal sites, a homepage host, a couple of blogging platforms, a forum, a guestbook, and servers for protocols most people have never heard of. Toward the end it gets stranger, with an NES game maker, a city made of text, a Brazilian pocket synth and a flight radar for the desk. As usual, no AI projects.</p><p>Same deal as always: I haven't tested everything here, and the last lines of each entry are what I'd be tempted to build, not promises.</p><h3 id="01-hn-personal-websites-directory">01. HN Personal Websites Directory</h3><p><a href="https://github.com/hnpwd/hnpwd">github.com/hnpwd/hnpwd</a></p><p>A community-maintained directory of personal websites by people from Hacker News, published at <a href="https://hnpwd.github.io">hnpwd.github.io</a> together with an OPML file of all their RSS feeds. The criteria are clear: the site has to belong to one person, have a good amount of English content and be fully under the author's control, and purely AI-generated content is out. You add a site with a pull request or an issue. Importing that OPML into a feed reader gives you a lot of reading in one click.</p><h3 id="02-bukmark-club">02. BUKMARK.CLUB</h3><p><a href="https://github.com/ttntm/bukmark.club">github.com/ttntm/bukmark.club</a></p><p>A directory of websites that keep a curated collection of bookmarks or links to other sites. That's the whole requirement for a listing. You fork the repo, add a Markdown file with your site's details, optionally with your own 88x31 button, and open a pull request. The lists on pablo.how, with pubnixes and Gemini capsules worth a visit, look like the kind of page this club collects.</p><h3 id="03-ringfairy">03. ringfairy</h3><p><a href="https://github.com/k3rs3d/ringfairy">github.com/k3rs3d/ringfairy</a></p><p>A webring generator in Rust, built like a static site generator made only for webrings. It writes plain HTML redirects, with no server code at all, so the whole ring can live on Neocities or GitHub Pages. It checks that every member site actually carries the ring's links, uses templates for everything and builds an OPML file of the members that have RSS. The trade-off is that every change means regenerating the ring, which the README says is quick. A ring for the handful of small sites I run would be an easy first test.</p><h3 id="04-the-repository-for-88x31-buttons">04. The Repository for 88x31 Buttons</h3><p><a href="https://github.com/transicle/88x31">github.com/transicle/88x31</a></p><p>An automatically updated collection of 88x31 buttons, the tiny banners of the 2000s web. The latest sync lists 86,882 unique buttons spread over 173 gallery pages, and new ones show up on their own when a site with buttons is added to the list. Good for finding a button to link to, or for scrolling through the old web's tiny ads for an hour. phlog.space has an 88x31 button of its own, and it would be fun to see it turn up in there one day.</p><h3 id="05-puppypad">05. PuppyPad</h3><p><a href="https://github.com/DeRusto/puppypad">github.com/DeRusto/puppypad</a></p><p>"A free old-web homepage host. Accidents welcome." Every member gets a subdomain for a static site, with a file manager, an in-browser editor, a hit counter, a guestbook and a webring link. There are four starter layouts, a style editor for colors, fonts, borders and even the cursor, and an upload option that shrinks big photos and strips their camera details. The public instance is at <a href="https://puppypad.org">puppypad.org</a>, and since the code is AGPL you can run your own, with quotas set in a config file. One of these for a small group of friends would be a good use for a spare VPS.</p><h3 id="06-mataroa">06. mataroa</h3><p><a href="https://github.com/mataroablog/mataroa">github.com/mataroablog/mataroa</a></p><p>"Naked blogging platform." Text only, with no themes, no custom CSS and no tags. Signing up takes a minute and doesn't ask for an email address, posts are Markdown, and there are no ads and no tracking. You can export the whole blog whenever you want, including as Zola or Hugo source files or as an epub, and it also mails you an export every month. The hosted version at <a href="https://mataroa.blog">mataroa.blog</a> has a free plan and a paid one that adds your own domain and posting by email. The epub export is a lovely idea: a blog that becomes a book you can read on an e-reader.</p><h3 id="07-ttbp">07. ttbp</h3><p><a href="https://github.com/modgethanc/ttbp">github.com/modgethanc/ttbp</a></p><p>The tilde.town blogging platform, a command-line hub for writing and reading blog posts on that server. You log in, type <code>feels</code>, and you can write your own entries and read what other residents wrote, a bit like LiveJournal living in a terminal. Each entry can go to a public page on your tilde, to tilde.town's gopher server, or stay private on the server. Something like this would sit well on runv.club.</p><h3 id="08-burrow">08. burrow</h3><p><a href="https://github.com/jamestomasino/burrow">github.com/jamestomasino/burrow</a></p><p>A helper for building and managing a gopher hole. <code>burrow phlog</code> asks for a title and opens a new post in your editor, <code>burrow gophermap</code> lets you edit a gophermap while writing the comments in plain text, and <code>burrow rss</code> builds a feed of your latest phlog posts. It can also pull, commit and push your gopher folder with git, which makes it easy to run from cron. The default config points at SDF, which tells you where it grew up. For anyone keeping a phlog on a pubnix, it saves a lot of typing.</p><h3 id="09-nexy">09. Nexy</h3><p><a href="https://github.com/YGGverse/nexy">github.com/YGGverse/nexy</a></p><p>A server for the Nex protocol, written in Rust. Nex is about as small as a protocol gets: you connect, send a path, and get back a document or a directory listing. Nexy can listen on the regular internet and on networks like Yggdrasil and Mycelium at the same time, has templates for directory listings and other responses, and writes its access log in the Common Log Format, so GoAccess can read it. Soledade's reading port, 1900, already answers Nex-style requests, so Nexy is a good reference to compare against.</p><h3 id="10-cozy">10. cozy</h3><p><a href="https://github.com/hachiya-kurisu/cozy">github.com/hachiya-kurisu/cozy</a></p><p>"surf the small web": a terminal browser for Gemini, Spartan and Nex, written in Go. Install it with <code>go install</code>, run <code>cozy</code>, or pass an address to go straight there, like <code>cozy nex://manatsu.town/</code>. <code>cozy -t day</code> forces the day theme. The plans in the README are client certificates, maybe images in the terminal, and "aesthetics". Pointing it at Soledade over Nex is the obvious first test.</p><h3 id="11-kennedy">11. Kennedy</h3><p><a href="https://github.com/acidus99/Kennedy">github.com/acidus99/Kennedy</a></p><p>A search engine for Gemini space, with its own crawler, backend and Gemini app server, built on ideas from the early web's crawlers and search engines. It does full-text search with stemming, suggests other queries when you get few results, understands searches like "cats AND dogs", and even searches images by their link text and paths. Try it at <code>gemini://kennedy.gemi.dev</code>, or through an HTTP proxy if you don't have a Gemini browser yet. With cozy from the entry above, that's a small-web setup in two programs.</p><h3 id="12-cerca">12. Cerca</h3><p><a href="https://github.com/cblgh/cerca">github.com/cblgh/cerca</a></p><p>"Lean forum software", written to power the Merveilles community forums. The README talks about missing the forums of the mid noughties: long discussions you visit on your own time, without being notified in the middle of something else. Threads can be public or private, invites come in one-time and multi-use batches, admin actions show up in a moderation log anyone logged in can read, and the big decisions need two admins or a week without a veto. There's RSS for new threads and posts. A small forum for the people who read this blog would be a fun experiment.</p><h3 id="13-joker-cgi-guestbook">13. Joker CGI Guestbook</h3><p><a href="https://github.com/dbohdan/joker-cgi-guestbook">github.com/dbohdan/joker-cgi-guestbook</a></p><p>A toy old-school CGI guestbook written in Joker, a dialect of Clojure. It's one self-contained script with the settings at the top, like CGI scripts used to have, and it keeps the entries in a Bolt database. Captchas are stateless and signed with HMAC. There's no admin page at all: to hide an entry, you open the database and add <code>"hide": true</code> to its JSON. It was written for the SDF Public Access UNIX System, and the author says plainly that it's a toy. phlog.space has guestbooks too, so this one is good to read side by side.</p><h3 id="14-qr-server">14. QR-Server</h3><p><a href="https://github.com/Xelckis/qr-server">github.com/Xelckis/qr-server</a></p><p>"A Full Web Server Inside a QR Code." A Linux web server written in x86_64 assembly, with a Brotli-compressed HTML page baked into the binary, and the whole thing weighs under 3 KB and fits in a single Version 40 QR code. It comes with an extractor in Go that reads the binary back out of the image without corrupting it. The README calls it "the peak of Jank Engineering", which is fair. That QR code printed on a business card would be the strangest homepage link around.</p><h3 id="15-outis">15. Outis</h3><p><a href="https://github.com/dtonon/outis">github.com/dtonon/outis</a></p><p>A small Go tool that answers spam with a fake "user unknown" bounce, so the sender believes your address doesn't exist. The bounce is modeled on a real Postfix one, following RFC 3464, and goes out from MAILER-DAEMON with a null sender when your server allows it. The name is Greek for "nobody", the name Odysseus gives the Cyclops. The README is honest about the limits: the message was already accepted, so it only works on senders that actually process bounces, and it's only believable when it comes from a domain you control. With your own domain and an SMTP account that can send as MAILER-DAEMON, it's a cheeky tool to keep around.</p><h3 id="16-wbtv">16. WBTV</h3><p><a href="https://github.com/WB2024/WBTV">github.com/WB2024/WBTV</a></p><p>Turns your own media library into live TV. You point it at your folders and it builds the channels and an EPG-style guide by itself, using the NFO tags in your library. Then come the details that make it feel like broadcast television: retro ad breaks, station idents, "coming up next" announcements read by a voice generated offline on your own machine, and a test card for the overnight closedown. It runs in one Docker container with your media mounted read-only, and there's an Android TV app. I've written about running a 24/7 TV channel from home, so the idents and the test card are the first things I'd look at.</p><h3 id="17-nesty">17. NESty</h3><p><a href="https://github.com/delacannon/nesty">github.com/delacannon/nesty</a></p><p>A Bitsy-style editor for making real NES games. You draw rooms, tiles, sprites and dialogs in the browser, and NESty compiles the game to 6502 assembly, assembles it into an iNES ROM right there and lets you play-test it in an embedded emulator. You can download the <code>.nes</code> file for any emulator or flash cart, and the generated <code>.asm</code> to study. Like Bitsy, the games are about walking around, talking to people and picking things up, one screen per room, with the console's real limits checked as you go. It's live at <a href="https://nesy.netlify.app/">nesy.netlify.app</a>. A tiny game set in Soledade, with the church tower and the lit windows, would make a good first cartridge.</p><h3 id="18-cybercity">18. CyberCity</h3><p><a href="https://github.com/MiguelCarino/CyberCity">github.com/MiguelCarino/CyberCity</a></p><p>Four walkable worlds drawn entirely in text characters: a rain-lit cyberpunk city, an 1880s frontier town, the Moon in the Apollo era and an Edo castle town on a wet evening. It's one HTML page and three small scripts, with no build step and no dependencies, and the sun goes round. All four worlds share the same engine and the same seed, so pressing 1, 2, 3 and 4 shows the same corner of the same street in four different places. The README goes deep into how each world is lit, and it's worth reading even if you never touch the code. Walk around at <a href="https://cybercity.carino.systems">cybercity.carino.systems</a>.</p><h3 id="19-jangada">19. Jangada</h3><p><a href="https://github.com/zednaked/jangada">github.com/zednaked/jangada</a></p><p>Alternative firmware for the M-VAVE FM-1, a pocket synth of about €70, "with a dark, industrial and Brazilian accent." Thirteen synth engines, four tracks, a modulation matrix, drones that evolve on their own, and drum kits that bring in maracatu, baião and coco, with alfaia, zabumba and cuíca. It's a fork of Felucca, and the name plays on that: a felucca is a Nile sailboat, a jangada is the Brazilian one. You can <a href="https://zednaked.github.io/jangada/webapp/studio/">play it in the browser</a> before flashing anything, and the README says the FM-1's boot area is never touched, so the official firmware is always one step away. Manguebeat on a pocket synth is reason enough to buy the hardware.</p><h3 id="20-esp32flight">20. esp32flight</h3><p><a href="https://github.com/theqkash/esp32flight">github.com/theqkash/esp32flight</a></p><p>A desk flight radar on a single ESP32 board with a touchscreen, also available as an Android app built from the same code. It lists the aircraft around you with airline logos, routes, altitude and speed, tells you which way to look and when a plane will pass over, and has a retro CRT radar view. The data comes from free community ADS-B sources or from your own receiver on the network, and it can also show the ISS, weather balloons and ships. You install it from the browser, and the cheapest supported board costs around $15. The README currently warns that the optional FlightAware integration has a bug that burns API credits, so leave that key empty. A little screen like that by a window, watching the sky, sounds like a good weekend.</p><h3 id="bonus-tabs">Bonus tabs</h3><p>A few more I refuse to close:</p><p><a href="https://github.com/quackduck/devzat">Devzat</a> · An SSH server that drops you into a chat room instead of a shell. Try <code>ssh devzat.hackclub.com</code>.</p><p><a href="https://github.com/kensanata/oddmuse">Oddmuse</a> · A wiki engine in a single Perl script, with no database.</p><p><a href="https://github.com/pubby/nesfab">NESFab</a> · A programming language made just for NES games, if NESty leaves you wanting more.</p><p><a href="https://github.com/ahmetomerv/photoc">photoc</a> · Command-line tools for photographers: find blurry shots and duplicates, make contact sheets, strip GPS before sharing.</p><p><a href="https://github.com/AndyHazz/bookshelf.koplugin">Bookshelf</a> · A home screen for KOReader, with covers, lists or spines standing like a real bookcase.</p><p><a href="https://github.com/t4t5/owly">Owly</a> · AirDrop between iPhones and Linux, on supported Wi-Fi hardware.</p><p><a href="https://github.com/514-labs/dnsglobe">dnsglobe</a> · Watch a DNS record propagate across 34 public resolvers on a world map, in your terminal.</p><p><a href="https://github.com/wintermute-cell/magick.css">magick.css</a> · A classless CSS file inspired by LaTeX and old tabletop RPG rulebooks.</p><p>That's issue five. Most of it is about places where people put their own words, and the rest makes noise or lights up a desk.</p><p>Found a strange repository, a forgotten tool or a small piece of software I should see? Send it my way. Reader suggestions are welcome for issue six. And if you make something with any of these, I'd like to see it.</p><p>Happy wandering!<br>Pablo</p>]]></content>
    </entry>
    <entry>
        <title>Emacs beyond the basics, part II: your first steps in Emacs Lisp</title>
        <link href="https://pablomurad.com/emacs-beyond-the-basics-part-2/"/>
        <id>https://pablomurad.com/emacs-beyond-the-basics-part-2/</id>
        <published>2026-11-09T14:13:36-03:00</published>
        <updated>2026-11-09T14:13:36-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="emacs"/>
        <summary>A gentle first step into Emacs Lisp: read the syntax, write your own commands, bind keys, use hooks and advice, build a quick-notes command and learn to debug and read Emacs’s own source. No programming background needed.</summary>
        <content type="html"><![CDATA[<p>The <a href="https://pablomurad.com/emacs-for-the-curious/">first Emacs guide</a> on this blog ended by pointing at a manual called "An Introduction to Programming in Emacs Lisp", which is how people go from using Emacs to bending it. This post is the friendly version of that step. No computer science background needed. If you can copy a line into your <code>init.el</code>, you can do everything here.</p><p>The goal is small and practical. By the end you'll read Lisp without panicking, write your own commands, give them keys, hook them into modes and know how to dig into any function Emacs has. That's most of what people ever do with Elisp.</p><h2 id="why-bother">Why bother</h2><p>Every key you press in Emacs runs a function written in Emacs Lisp. Your config is Lisp. The packages you install are Lisp. Once you can read a little of it, three things change:</p><ul>
<li value="1">Your <code>init.el</code> stops being a list of spells copied from the internet. You know what each line does, and you can fix it when it breaks.</li>
<li value="2">When something annoys you, you can write the fix yourself, usually in five or ten lines.</li>
<li value="3">The source of every command is one click away, so Emacs becomes its own textbook.</li>
</ul><h2 id="where-to-try-things">Where to try things</h2><p>You don't need to create a file to experiment. Emacs gives you three playgrounds.</p><p>The <code>*scratch*</code> buffer opens with Emacs, in a mode made for this. Type an expression, put the cursor right after it and press <code>C-j</code>. The result gets printed on the next line.</p><p><code>M-:</code> asks for an expression in the minibuffer, runs it and shows the result at the bottom of the screen. It works from any buffer, which makes it great for quick questions like "what's the value of this variable right now?".</p><p><code>C-x C-e</code> runs the expression right before the cursor, in any Lisp file. This is the one you'll use the most in your <code>init.el</code>: change a line, put the cursor after it, <code>C-x C-e</code>, and the change is live without restarting.</p><p>And if you like a proper REPL, <code>M-x ielm</code> gives you one, with a prompt and history.</p><h2 id="reading-lisp">Reading Lisp</h2><p>Lisp looks scary because of the parentheses, but the rule is simple. A list in parentheses is a call: the first thing is the function, the rest are its arguments.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">+</span><span style="color:#4C9A91"> 1</span><span style="color:#4C9A91"> 2</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; =&gt; 3</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">*</span><span style="color:#4C9A91"> 2</span><span style="color:#666666"> (</span><span style="color:#CB7676">+</span><span style="color:#4C9A91"> 3</span><span style="color:#4C9A91"> 4</span><span style="color:#666666">))</span><span style="color:#758575DD"> ; =&gt; 14</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">concat</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Hello, </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Emacs</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; =&gt; "Hello, Emacs"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">upcase</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">hello</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; =&gt; "HELLO"</span></span></code></pre></div><p>The inner parentheses run first, so <code>(* 2 (+ 3 4))</code> becomes <code>(* 2 7)</code>, which becomes 14. That's the whole grammar. Everything else is vocabulary.</p><p>A semicolon starts a comment. Strings go in double quotes. <code>t</code> means true, and <code>nil</code> means false and also "empty list". Those two show up everywhere.</p><p>Sometimes you want a list as data, not as a call. That's what the quote is for:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">'(</span><span style="color:#CB7676">+</span><span style="color:#4C9A91"> 1</span><span style="color:#4C9A91"> 2</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; =&gt; (+ 1 2), just a list with three things in it</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">+</span><span style="color:#4C9A91"> 1</span><span style="color:#4C9A91"> 2</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; =&gt; 3</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#4D9375">car</span><span style="color:#666666"> '(</span><span style="color:#DBD7CAEE">apples pears plums</span><span style="color:#666666">))</span><span style="color:#758575DD"> ; =&gt; apples, the first item</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#4D9375">cdr</span><span style="color:#666666"> '(</span><span style="color:#DBD7CAEE">apples pears plums</span><span style="color:#666666">))</span><span style="color:#758575DD"> ; =&gt; (pears plums), the rest</span></span></code></pre></div><p>The same quote is why config files are full of things like <code>'modus-vivendi</code> or <code>#'display-line-numbers-mode</code>. The <code>'</code> says "this name, as it is". The <code>#'</code> is the same idea for names of functions, and it helps Emacs warn you when you misspell one.</p><p>A note about indentation: Lisp doesn't care about it. The blocks of code in this post are written flat, and Emacs will indent them properly for you. Paste the code, select it and press <code>TAB</code>, or use <code>C-M-\</code>.</p><h2 id="variables">Variables</h2><p><code>setq</code> sets a variable. <code>let</code> creates variables that only exist inside its parentheses:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> my-name </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Pablo</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">let</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">x </span><span style="color:#4C9A91">10</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">y </span><span style="color:#4C9A91">32</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">+</span><span style="color:#DBD7CAEE"> x y</span><span style="color:#666666">))</span><span style="color:#758575DD"> ; =&gt; 42, and x and y vanish after this</span></span></code></pre></div><p><code>format</code> builds a string with placeholders, <code>%s</code> for anything and <code>%d</code> for whole numbers:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">format</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Hi, </span><span style="color:#C99076">%s</span><span style="color:#C98A7D">! You have </span><span style="color:#C99076">%d</span><span style="color:#C98A7D"> buffers open.</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> my-name </span><span style="color:#666666">(</span><span style="color:#4D9375">length</span><span style="color:#666666"> (</span><span style="color:#B8A965">buffer-list</span><span style="color:#666666">)))</span></span></code></pre></div><p>For settings that Emacs or a package offers as options (the ones you'd see in <code>M-x customize</code>), Emacs 29 added <code>setopt</code>. It works like <code>setq</code>, but it also runs any code the option needs when it changes, and it warns you when the value has the wrong type:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">setopt fill-column </span><span style="color:#4C9A91">80</span><span style="color:#666666">)</span></span></code></pre></div><p>A simple habit: <code>setopt</code> for options, <code>setq</code> for everything else.</p><h2 id="your-first-command">Your first command</h2><p>A function is made with <code>defun</code>. Give it a name, a list of arguments, a short description and a body. Add <code>(interactive)</code> and it becomes a command, something you can run with <code>M-x</code> or bind to a key.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-insert-date</span><span style="color:#666666"> ()</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Insert today's date at point, like 2026-11-06.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">interactive</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">insert</span><span style="color:#666666"> (</span><span style="color:#B8A965">format-time-string</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">%Y-%m-%d</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span></code></pre></div><p>Put the cursor after the last parenthesis and press <code>C-x C-e</code>. Now <code>M-x my-insert-date</code> works. And because you wrote a description, <code>C-h f my-insert-date</code> shows it, just like any built-in command.</p><p>Two habits worth having from day one. First, start your function names with a prefix, like <code>my-</code>, so they never clash with something Emacs or a package already uses. Second, always write the description. Future you will read it.</p><h2 id="giving-it-a-key">Giving it a key</h2><p>Emacs 29 added a clean way to bind keys:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">keymap-global-set </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c d</span><span style="color:#C98A7D77">"</span><span style="color:#666666"> #'</span><span style="color:#C99076">my-insert-date</span><span style="color:#666666">)</span></span></code></pre></div><p>Now <code>C-c d</code> inserts the date anywhere. The choice of <code>C-c</code> and a letter is on purpose. Emacs keeps every <code>C-c</code> followed by a plain letter for you, the user. Neither Emacs nor well-behaved packages will ever take those keys, so they're the safe place for your own commands.</p><p>You can also bind a key only inside one mode, using that mode's keymap:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">with-eval-after-load</span><span style="color:#666666"> '</span><span style="color:#C99076">org</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">keymap-set org-mode-map </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c n</span><span style="color:#C98A7D77">"</span><span style="color:#666666"> #'</span><span style="color:#C99076">my-new-note</span><span style="color:#666666">))</span></span></code></pre></div><p><code>with-eval-after-load</code> waits until Org is actually loaded before touching its keymap. Without it, Emacs would complain that <code>org-mode-map</code> doesn't exist yet. You'll write <code>my-new-note</code> in a minute.</p><h2 id="asking-the-user-for-something">Asking the user for something</h2><p><code>interactive</code> can do more than mark a function as a command. Give it a code and Emacs will ask the user for the arguments. <code>"s"</code> asks for a string:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-greet</span><span style="color:#666666"> (</span><span style="color:#BD976A">name</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Say hello to NAME.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">interactive</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">sYour name: </span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">message</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Hello, </span><span style="color:#C99076">%s</span><span style="color:#C98A7D">!</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> name</span><span style="color:#666666">)</span><span style="color:#666666">)</span></span></code></pre></div><p>Run <code>M-x my-greet</code> and Emacs prompts "Your name:" in the minibuffer. Other codes ask for a number (<code>"n"</code>), a file (<code>"f"</code>), a buffer (<code>"b"</code>) and more. <code>C-h f interactive</code> lists them all.</p><h2 id="working-with-text">Working with text</h2><p>Inside a buffer, the cursor position is called the point, and a few functions do most of the work:</p><ul>
<li value="1">
<code>(point)</code>: where the cursor is, as a number.</li>
<li value="2">
<code>(goto-char 1)</code>: move the cursor. <code>(point-min)</code> and <code>(point-max)</code> are the start and the end of the buffer.</li>
<li value="3">
<code>(insert "text")</code>: write at the cursor.</li>
<li value="4">
<code>(buffer-string)</code>: the whole buffer as a string.</li>
<li value="5">
<code>(count-words (point-min) (point-max))</code>: count words, which is a built-in command too.</li>
</ul><p>One more is worth learning early: <code>save-excursion</code>. It runs some code and then puts the cursor back where it was, so your command doesn't leave the user lost in the file.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-add-todo-on-top</span><span style="color:#666666"> ()</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Insert a TODO line at the top of the buffer, without moving the cursor.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">interactive</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">save-excursion</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">goto-char</span><span style="color:#666666"> (</span><span style="color:#B8A965">point-min</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">insert</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">TODO: review this file</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span></code></pre></div><h2 id="making-decisions-and-repeating-things">Making decisions and repeating things</h2><p><code>if</code> takes a condition, what to do when it's true and what to do when it isn't. <code>when</code> and <code>unless</code> are shortcuts for when you only need one side. <code>cond</code> is a chain of options, checked from top to bottom:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">let</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">n </span><span style="color:#4C9A91">7</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#4D9375">cond</span><span style="color:#666666"> ((</span><span style="color:#CB7676">&lt;</span><span style="color:#DBD7CAEE"> n </span><span style="color:#4C9A91">5</span><span style="color:#666666">)</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">small</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">((</span><span style="color:#CB7676">&lt;</span><span style="color:#DBD7CAEE"> n </span><span style="color:#4C9A91">10</span><span style="color:#666666">)</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">medium</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C99076">t</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">big</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span><span style="color:#758575DD"> ; =&gt; "medium"</span></span></code></pre></div><p>The <code>t</code> at the end works as "otherwise", since <code>t</code> is always true.</p><p><code>dolist</code> walks through a list. This one goes over every open buffer and tells you which files have unsaved changes:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-list-unsaved</span><span style="color:#666666"> ()</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Show which file buffers have unsaved changes.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">interactive</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#4D9375">dolist</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">b </span><span style="color:#666666">(</span><span style="color:#B8A965">buffer-list</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#4D9375">when</span><span style="color:#666666"> (</span><span style="color:#4D9375">and</span><span style="color:#666666"> (</span><span style="color:#B8A965">buffer-file-name</span><span style="color:#DBD7CAEE"> b</span><span style="color:#666666">)</span><span style="color:#666666"> (</span><span style="color:#B8A965">buffer-modified-p</span><span style="color:#DBD7CAEE"> b</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">message</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Unsaved: </span><span style="color:#C99076">%s</span><span style="color:#C98A7D77">"</span><span style="color:#666666"> (</span><span style="color:#B8A965">buffer-name</span><span style="color:#DBD7CAEE"> b</span><span style="color:#666666">))</span><span style="color:#666666">)))</span></span></code></pre></div><p>The messages pile up in the <code>*Messages*</code> buffer, which you can open with <code>C-h e</code>.</p><h2 id="a-real-command-quick-notes">A real command: quick notes</h2><p>Time to put it all together. This command asks for a title, turns it into a clean file name and opens a new Org note in your notes folder, already with a title and the date.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defvar</span><span style="color:#80A665"> my-notes-directory</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">~/notes</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Where </span><span style="color:#666666">`</span><span style="color:#C98A7D">my-new-note</span><span style="color:#666666">'</span><span style="color:#C98A7D"> keeps its files.</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-slugify</span><span style="color:#666666"> (</span><span style="color:#BD976A">title</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Turn TITLE into a lowercase, dash-separated slug.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">let*</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">s </span><span style="color:#666666">(</span><span style="color:#B8A965">downcase</span><span style="color:#DBD7CAEE"> title</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">s </span><span style="color:#666666">(</span><span style="color:#B8A965">replace-regexp-in-string</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">[^a-z0-9]+</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">-</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> s</span><span style="color:#666666">)))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">string-trim</span><span style="color:#DBD7CAEE"> s </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">-</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">-</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-new-note</span><span style="color:#666666"> (</span><span style="color:#BD976A">title</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Create a new Org note called TITLE in </span><span style="color:#666666">`</span><span style="color:#C98A7D">my-notes-directory</span><span style="color:#666666">'</span><span style="color:#C98A7D">.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">interactive</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">sNote title: </span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">let*</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">date </span><span style="color:#666666">(</span><span style="color:#B8A965">format-time-string</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">%Y-%m-%d</span><span style="color:#C98A7D77">"</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file </span><span style="color:#666666">(</span><span style="color:#B8A965">expand-file-name</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">concat</span><span style="color:#DBD7CAEE"> date </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">-</span><span style="color:#C98A7D77">"</span><span style="color:#666666"> (</span><span style="color:#DBD7CAEE">my-slugify title</span><span style="color:#666666">)</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#DBD7CAEE">my-notes-directory</span><span style="color:#666666">)))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">make-directory</span><span style="color:#DBD7CAEE"> my-notes-directory </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">find-file</span><span style="color:#DBD7CAEE"> file</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#4D9375">when</span><span style="color:#666666"> (</span><span style="color:#CB7676">=</span><span style="color:#666666"> (</span><span style="color:#B8A965">buffer-size</span><span style="color:#666666">)</span><span style="color:#4C9A91"> 0</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">insert</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">#+title: </span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> title </span><span style="color:#C98A7D77">"</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">#+date: </span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> date </span><span style="color:#C98A7D77">"</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D77">"</span><span style="color:#666666">))))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">keymap-global-set </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c n</span><span style="color:#C98A7D77">"</span><span style="color:#666666"> #'</span><span style="color:#C99076">my-new-note</span><span style="color:#666666">)</span></span></code></pre></div><p>Press <code>C-c n</code>, type "Ideas for the weekend", and you're editing <code>2026-11-06-ideas-for-the-weekend.org</code>, with the title and the date at the top. If the file already exists, it just opens, without writing the header twice.</p><p>A few new pieces showed up here:</p><ul>
<li value="1">
<code>defvar</code> creates a variable with a description, the polite way to define a setting.</li>
<li value="2">
<code>let*</code> is like <code>let</code>, except each variable can use the ones before it. Here <code>s</code> gets rewritten in two steps.</li>
<li value="3">
<code>replace-regexp-in-string</code> swaps every run of characters that aren't letters or numbers for a dash.</li>
<li value="4">
<code>string-trim</code> removes leftover dashes from the edges.</li>
<li value="5">
<code>make-directory</code> with <code>t</code> creates the folder, and doesn't complain if it already exists.</li>
</ul><p>Read it slowly, change something, run it again. That loop is how you learn Elisp.</p><h2 id="hooks-run-code-when-something-happens">Hooks: run code when something happens</h2><p>A hook is a list of functions that Emacs runs at a specific moment. Every mode has one, named after the mode with <code>-hook</code> at the end. This is how you say "every time I open a text file, do this":</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">text-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">visual-line-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span><span style="color:#666666"> #'</span><span style="color:#C99076">display-line-numbers-mode</span><span style="color:#666666">)</span></span></code></pre></div><p>The first line wraps long lines nicely in text files. The second turns on line numbers in any programming mode, since all of them build on <code>prog-mode</code>.</p><p>When you need more than one function call, use a <code>lambda</code>, which is a function without a name:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">add-hook</span><span style="color:#666666"> '</span><span style="color:#C99076">prog-mode-hook</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">lambda</span><span style="color:#666666"> ()</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">setq-local</span><span style="color:#DBD7CAEE"> fill-column </span><span style="color:#4C9A91">100</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">electric-pair-local-mode</span><span style="color:#4C9A91"> 1</span><span style="color:#666666">)))</span></span></code></pre></div><p><code>setq-local</code> changes the variable only in that buffer, so your Python files can have one width and your notes another.</p><p>There are hooks for almost everything: <code>before-save-hook</code>, <code>after-save-hook</code>, <code>kill-emacs-hook</code>, <code>emacs-startup-hook</code>. <code>C-h v</code> and the word "hook" will show you the list.</p><h2 id="advice-changing-a-command-you-didn-t-write">Advice: changing a command you didn't write</h2><p>Sometimes you want a built-in command to do one extra thing. Advice lets you attach your own function before, after or around it:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">defun</span><span style="color:#80A665"> my-announce-save</span><span style="color:#666666"> (</span><span style="color:#4D9375">&amp;rest</span><span style="color:#BD976A"> _</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Tell me which buffer was just saved.</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">message</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Saved </span><span style="color:#C99076">%s</span><span style="color:#C98A7D77">"</span><span style="color:#666666"> (</span><span style="color:#B8A965">buffer-name</span><span style="color:#666666">))</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">advice-add</span><span style="color:#666666"> '</span><span style="color:#C99076">save-buffer</span><span style="color:#666666"> :</span><span style="color:#C99076">after</span><span style="color:#666666"> #'</span><span style="color:#C99076">my-announce-save</span><span style="color:#666666">)</span></span></code></pre></div><p>Now every save shows a little message. To undo it, <code>advice-remove</code> with the same arguments.</p><p>Advice is powerful, and that's exactly why it deserves care. Reach for a hook first. If there isn't one, then advice is fine, but keep each piece small and give it a clear name, because advice is invisible when you're reading the original command's code.</p><h2 id="reading-emacs-s-own-code">Reading Emacs's own code</h2><p>This is where the investment pays off. <code>C-h f</code> describes any function, and the description has a link to the file where it's defined. Click it and you're reading the real source. <code>C-h k</code> does the same for a key: press <code>C-h k</code> and then <code>C-x C-s</code>, follow the link, and you'll see how Emacs saves a file.</p><p>Three other help commands that pair well with Elisp:</p><ul>
<li value="1">
<code>C-h o</code>: describe any symbol, function or variable, whatever it is.</li>
<li value="2">
<code>C-h v</code>: describe a variable and show its current value.</li>
<li value="3">
<code>C-h i</code>: the manuals, including the Elisp reference, inside Emacs.</li>
</ul><p>You won't understand everything you read at first. That's fine. Skim, look for the parts you recognize, and every week you'll recognize more.</p><h2 id="when-things-break">When things break</h2><p>Errors in Elisp are normal, and Emacs has good tools for them.</p><p><code>M-x toggle-debug-on-error</code> makes Emacs open a backtrace whenever an error happens. It looks dense, but the top lines usually point straight at the function that failed.</p><p>For your own functions, there's Edebug. Put the cursor inside a <code>defun</code> and press <code>C-u C-M-x</code>. The next time the function runs, Emacs stops at the first step and lets you walk through it with <code>SPC</code>, one expression at a time, showing each value as it goes. <code>q</code> gets you out.</p><p>If your <code>init.el</code> breaks so badly that Emacs starts in a strange state, remember <code>emacs -Q</code>, which starts without any config. Open the file, fix it, restart. And <code>emacs --debug-init</code> starts with your config and opens the backtrace at the first error, which usually tells you the exact line.</p><p>Finally, <code>M-x checkdoc</code> reviews the descriptions in your file and points out small style problems. It's a good teacher.</p><h2 id="organizing-your-init-el">Organizing your init.el</h2><p>Once you have a few functions, keep them tidy. Two simple rules go a long way.</p><p>Group things by topic, with a comment on top of each group, and keep your own functions together in one section. Use <code>;;;</code> for section titles. Emacs even understands them: <code>M-x outline-minor-mode</code> lets you fold the file by those headings.</p><p>And let <code>use-package</code> do the bookkeeping for packages and built-in features alike. It's built into Emacs since version 29:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#B8A965"> org</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">hook</span><span style="color:#666666"> (</span><span style="color:#B8A965">org-mode</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> visual-line-mode</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c a</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> org-agenda</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">config</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-log-done </span><span style="color:#666666">'</span><span style="color:#C99076">time</span><span style="color:#666666">))</span></span></code></pre></div><p><code>:hook</code> adds hooks, <code>:bind</code> sets keys and <code>:config</code> runs code after the package loads. It's the same Lisp you learned above, organized into labeled boxes.</p><h2 id="where-to-go-next">Where to go next</h2><p>Inside Emacs, <code>C-h i</code> opens the manuals. Look for "An Introduction to Programming in Emacs Lisp", written for people who have never programmed. It's long, gentle and full of small exercises. When you want the details, the "Emacs Lisp Reference Manual" sits right next to it. Both are also online at <a href="https://www.gnu.org/software/emacs/manual/">gnu.org/software/emacs/manual</a>.</p><p>A good next exercise: pick something you do by hand at least twice a week, and turn it into a command. Inserting a template, opening a set of files, cleaning up a pasted text. Small commands that fit your own habits are where Elisp starts to feel personal.</p><p>This is part II of a series. <a href="https://pablomurad.com/emacs-beyond-the-basics-part-1/">Part I</a> is about living inside Emacs: feeds, gopher, the fediverse, chat and mail. <a href="https://pablomurad.com/emacs-beyond-the-basics-part-3/">Part III</a> turns Emacs into a full programming setup.</p>]]></content>
    </entry>
    <entry>
        <title>50 Obsidian tricks that need zero plugins</title>
        <link href="https://pablomurad.com/50-obsidian-tricks-zero-plugins/"/>
        <id>https://pablomurad.com/50-obsidian-tricks-zero-plugins/</id>
        <published>2026-11-09T09:47:00-03:00</published>
        <updated>2026-11-08T04:56:32-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="obsidian"/>
        <summary>Fifty Obsidian tricks hiding in a fresh install, no community plugins needed: search operators, block links and embeds, callouts, multiple cursors, Bases formulas, file recovery, the new CLI and more.</summary>
        <content type="html"><![CDATA[<p>This blog already has a post about <a href="https://pablomurad.com/an-obsidian-vault-that-lasts/">building a vault that lasts</a> and two lists with a hundred plugins between them (<a href="https://pablomurad.com/50-obsidian-plugins-worth-your-time/">one</a>, <a href="https://pablomurad.com/50-more-obsidian-plugins-worth-your-time/">two</a>). This one goes the other way. Every trick below works in a fresh install, with zero community plugins. A lot of people never find them because they're tucked into a setting, a key combination or a core plugin that's switched off.</p><p>Everything was checked against Obsidian 1.15, the current version as I write this. Where a trick only exists in recent versions, I say which one. Keys are written for Windows and Linux; on a Mac, read Ctrl as Cmd and Alt as Option.</p><h2 id="search-like-you-mean-it">Search like you mean it</h2><p><strong>1. Search has operators.</strong> The search pane (Ctrl+Shift+F) understands much more than words. A few that change how you use it:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>file:recipe (notes with "recipe" in the file name)</span></span>
<span class="line"><span>path:"Projects/2026" (notes inside that folder)</span></span>
<span class="line"><span>tag:#todo (the tag, ignoring code blocks)</span></span>
<span class="line"><span>line:(server backup) (both words on the same line)</span></span>
<span class="line"><span>section:(server backup) (both words under the same heading)</span></span>
<span class="line"><span>task-todo:call (unfinished tasks that mention "call")</span></span>
<span class="line"><span>task-done:call (finished ones)</span></span>
<span class="line"><span>match-case:HappyCat (case-sensitive search)</span></span></code></pre></div><p>You can combine them. A space means AND, <code>OR</code> means or, a minus sign excludes (<code>-draft</code>), quotes search an exact phrase and parentheses group things. And if a query gets too clever to read, turn on the "Explain search term" toggle in the search pane and Obsidian describes it in plain words.</p><p><strong>2. Search by property.</strong> Square brackets search your properties. <code>[status:Draft]</code> finds notes with that status, <code>[status:Draft OR Published]</code> finds either, <code>[duration:&lt;5]</code> compares numbers, and <code>[aliases]</code> alone finds any note that has the property at all.</p><p><strong>3. Regular expressions work too.</strong> Put the pattern between slashes. <code>path:/\d{4}-\d{2}-\d{2}/</code> finds every file with a date in its path.</p><p><strong>4. Put a live search inside a note.</strong> A code block with <code>query</code> as its language runs a search and shows the results right there, always up to date:</p><div class="code-block" data-lang="query"><pre data-language="query" data-lang="query">tag:#read -tag:#done</pre></div><p>It's a reading list that maintains itself. One caveat: Obsidian Publish doesn't render these.</p><h2 id="links-and-embeds-you-can-aim">Links and embeds you can aim</h2><p><strong>5. Link to a heading, not just a note.</strong> <code>[[Server notes#Backups]]</code> jumps straight to that section. <code>[[#Backups]]</code> does the same inside the current note.</p><p><strong>6. Link to a single paragraph.</strong> Put a space and an ID at the end of a paragraph, like <code>^why-restic</code>, then link to it with <code>[[Server notes#^why-restic]]</code>. You don't even have to write the ID yourself: type <code>[[Server notes#^</code> and Obsidian lists the paragraphs to pick from, and creates the ID for you.</p><p><strong>7. Search the whole vault from inside a link.</strong> Type <code>[[##</code> and you're searching every heading in every note. Type <code>[[^^</code> and you're searching every paragraph. It's the fastest way to link to a thought when you don't remember which note it lives in.</p><p><strong>8. Embed only the part you need.</strong> Add <code>!</code> before any of those links and the content shows up inline. <code>![[Server notes#Backups]]</code> shows just that section, <code>![[Server notes#^why-restic]]</code> shows just that paragraph. Edit the original and every embed follows.</p><p><strong>9. Size your images.</strong> <code>![[photo.jpg|300]]</code> makes an image 300 pixels wide and keeps its proportions. <code>![[photo.jpg|640x480]]</code> sets both sides. Inside a table, the pipe needs a backslash before it, or the table breaks.</p><p><strong>10. Open a PDF on the right page.</strong> <code>![[manual.pdf#page=12]]</code> embeds the PDF already on page 12. Add a height with <code>#page=12&amp;height=400</code>.</p><p><strong>11. Change what a link says.</strong> <code>[[Artificial intelligence|AI]]</code> links to the note but shows "AI" in the text.</p><h2 id="properties-that-do-more-than-you-think">Properties that do more than you think</h2><p><strong>12. Add a property from the keyboard.</strong> Ctrl+; adds a new property to the current note. Typing <code>---</code> on the first line of a note starts the properties block too.</p><p><strong>13. Give a note several names.</strong> The <code>aliases</code> property lists other names for a note. They show up in link suggestions with a little curved arrow, and picking one writes the link with the alias as display text. The Backlinks pane also catches unlinked mentions of the aliases, so a note called "Artificial intelligence" with the alias "AI" finds every loose "AI" in your vault.</p><p><strong>14. Style a single note.</strong> The <code>cssclasses</code> property adds a CSS class to one note. Pair it with a CSS snippet and that note can look different from the rest. The example from the official docs puts a red border around the images of one note. In the note:</p><div class="code-block" data-lang="YAML"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="yaml"><code><span class="line"><span style="color:#80A665">---</span></span>
<span class="line"><span style="color:#B8A965">cssclasses</span><span style="color:#666666">:</span></span>
<span class="line"><span style="color:#666666">-</span><span style="color:#C98A7D"> red-border</span></span>
<span class="line"><span style="color:#80A665">---</span></span></code></pre></div><p>And in a file inside <code>.obsidian/snippets</code>:</p><div class="code-block" data-lang="CSS"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="css"><code><span class="line"><span style="color:#666666">.</span><span style="color:#BD976A">red-border</span><span style="color:#4D9375"> img</span><span style="color:#666666"> {</span><span style="color:#B8A965"> border-color</span><span style="color:#666666">:</span><span style="color:#666666"> #</span><span style="color:#C99076">ff0000</span><span style="color:#666666">;</span><span style="color:#B8A965"> border-style</span><span style="color:#666666">:</span><span style="color:#C99076"> solid</span><span style="color:#666666">;</span><span style="color:#666666"> }</span></span></code></pre></div><p>Then go to Settings, Appearance, CSS snippets, press the reload button and turn the snippet on.</p><h2 id="writing-tricks">Writing tricks</h2><p><strong>15. Callouts that fold.</strong> A callout is a quote with a type: <code>&gt; [!tip]</code>, <code>&gt; [!warning]</code>, <code>&gt; [!question]</code> and a dozen more. Write a title after the type to replace the default one. Add <code>-</code> right after the type and it starts folded, <code>+</code> and it starts open:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>&gt; [!faq]- Why is the backup so slow?</span></span>
<span class="line"><span>&gt; Because it's checking every file the first time.</span></span></code></pre></div><p>That's a FAQ that opens on click, in plain Markdown. Callouts also nest, with <code>&gt; &gt; [!todo]</code> inside another one, and in Live Preview you can right-click the title to switch its type.</p><p><strong>16. Write things only you can see.</strong> Anything between <code>%%</code> and <code>%%</code> is a comment. It stays in the file but disappears in Reading view. It works inline or across several lines. Just remember to close it, because an open <code>%%</code> hides the rest of the note.</p><p><strong>17. Highlights come in colors now.</strong> <code>==text==</code> highlights as always. Since version 1.14, put a colored circle emoji right after the opening <code>==</code> and the highlight takes that color. Type <code>==</code> and Obsidian suggests the colors.</p><p><strong>18. Footnotes, two ways.</strong> The classic way is <code>[^1]</code> in the text and <code>[^1]: the note</code> anywhere below. The lazy way is inline: <code>^[the note goes right here]</code>. Inline footnotes only show up in Reading view, not in Live Preview, so check there.</p><p><strong>19. Diagrams from text.</strong> A code block with <code>mermaid</code> as its language draws flowcharts, sequence diagrams, timelines and more. Since 1.15, diagrams follow your theme's colors and you can zoom and pan inside them.</p><h2 id="keys-that-save-hours">Keys that save hours</h2><p><strong>20. Many cursors at once.</strong> Alt+click adds another cursor. Shift+Alt+drag, or dragging with the middle mouse button, makes a rectangular selection, which is great for editing a column of text. Escape goes back to one cursor.</p><p><strong>21. Small keys worth memorizing.</strong></p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>Ctrl+L: turn a line into a checkbox, and tick it</span></span>
<span class="line"><span>Ctrl+Shift+V: paste without formatting</span></span>
<span class="line"><span>Ctrl+Shift+K: delete the current line</span></span>
<span class="line"><span>Ctrl+C with nothing selected: copy the whole paragraph</span></span>
<span class="line"><span>Ctrl+E: switch between editing and reading</span></span></code></pre></div><p><strong>22. Paste a link over text.</strong> Select a word, paste a URL, and Obsidian turns it into a Markdown link with that word as the text.</p><p><strong>23. Give a key to anything.</strong> Every command in Obsidian can get a hotkey in Settings, Hotkeys. Some useful commands come with no key at all. "Move line up" and "Move line down" are the best examples. Give them Alt+Up and Alt+Down and you'll wonder how you lived without them. "Fold more" and "Fold less" are good candidates too.</p><p><strong>24. Move whole sections around.</strong> Open the Outline pane and drag a heading to another position. The whole section, text and subheadings, moves with it.</p><h2 id="getting-around">Getting around</h2><p><strong>25. The quick switcher knows more than one trick.</strong> Ctrl+O opens it. Enter on a name that doesn't exist creates the note. Shift+Enter creates a note with exactly what you typed, even if other notes match. Ctrl+Enter opens the result in a new tab. And with the box empty, Down and then Enter takes you back to the note you were on before.</p><p><strong>26. Pin your favorite commands.</strong> The command palette (Ctrl+P) is fuzzy, so "scf" finds "Save current file". In Settings, Command palette, you can pin commands so they always sit at the top.</p><p><strong>27. Open links where you want them.</strong> Ctrl+click opens a link in a new tab. Ctrl+Alt+click opens it in a split. Ctrl+Alt+Shift+click opens it in a new window.</p><p><strong>28. Tabs have tricks too.</strong> Ctrl+1 to Ctrl+8 jump to the first eight tabs, Ctrl+9 to the last. Ctrl+Shift+T reopens the tab you just closed. Right-click a tab to pin it. And in the tab group menu, "Stack notes" turns your tabs into sliding panels, nice for reading several notes side by side.</p><p><strong>29. Save your layouts.</strong> The Workspaces core plugin saves which notes are open, how they're split and how the sidebars look. Make one for writing, one for research, one for the weekly review, and switch between them in a second.</p><p><strong>30. Bookmark more than notes.</strong> The Bookmarks core plugin saves searches, headings and single paragraphs, not just files. A saved search that you open every Monday is a tiny dashboard.</p><p><strong>31. Peek without opening.</strong> With Page preview on, hold Ctrl and hover a link in the editor to see the note in a popup.</p><h2 id="core-plugins-people-forget">Core plugins people forget</h2><p><strong>32. Split and merge notes.</strong> Note composer has two moves. Select text and run "Extract current selection" to send it to a new note, leaving a link behind. Or merge a whole note into another one when you realize they're about the same thing.</p><p><strong>33. Turn mentions into links.</strong> At the bottom of the Backlinks pane, "Unlinked mentions" lists every place that mentions the note by name without linking it. One click links each one. You can also show backlinks right at the end of the note with the "Toggle backlinks in document" command.</p><p><strong>34. Meet an old note.</strong> Random note opens any note from your vault. It's an easy way to rediscover things you forgot you wrote.</p><p><strong>35. Talk instead of type.</strong> Audio recorder records from your microphone and embeds the recording at the end of the current note. Good for ideas on the go.</p><p><strong>36. Slash commands.</strong> Turn on the Slash commands core plugin and type <code>/</code> at the start of a line to run any command without leaving the keyboard.</p><p><strong>37. Templates know dates.</strong> Inside a template, <code>{{title}}</code>, <code>{{date}}</code> and <code>{{time}}</code> fill themselves in, and the date takes any format, like <code>{{date:dddd, D MMMM YYYY}}</code>. The docs give one tip worth repeating: edit your templates in Source mode, because Live Preview can mess with the variables inside properties.</p><h2 id="bases-past-the-basics">Bases, past the basics</h2><p>The vault post used Bases for two simple tables. They can do a lot more.</p><p><strong>38. Formulas.</strong> A Base can have columns calculated from your properties. A few examples from the official docs:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>file.mtime &gt; now() - "1 week"</span></span>
<span class="line"><span>file.mtime.relative()</span></span>
<span class="line"><span>if(price, price.toFixed(2) + " dollars")</span></span></code></pre></div><p>The first is true for notes changed in the last week, the second shows something like "3 days ago" and the third formats a price only when the note has one.</p><p><strong>39. Summaries and groups.</strong> Right-click a column and pick "Summarize" to get a sum, an average, the earliest date, how many boxes are checked and more. Group the rows by any property, and fold the groups you don't need.</p><p><strong>40. Backlinks as a table.</strong> In a Base embedded in a note, <code>this</code> means the note that holds the embed. A filter with <code>file.hasLink(this.file)</code> lists every note that links to the current one, with whatever columns you want. Make that Base once and embed it in your hub notes with <code>![[Backlinks.base]]</code>.</p><p><strong>41. More than tables.</strong> Bases also show cards, lists and, since version 1.14, a kanban board. And the results menu exports any view to CSV.</p><h2 id="canvas-and-graph">Canvas and graph</h2><p><strong>42. Turn a card into a note.</strong> In a Canvas, right-click a text card and choose "Convert to file". The card becomes a real note, with backlinks and everything. Alt+drag duplicates a card, Shift+1 zooms to fit everything and Shift+2 zooms to what you've selected.</p><p><strong>43. Paint the graph.</strong> In the graph view settings, Groups lets you color nodes by any search. Color <code>path:Projects</code> in one color and <code>tag:#person</code> in another, and the graph starts to show you something. For a smaller picture, "Open local graph" shows only the neighbors of the current note, with a slider for how far to look.</p><h2 id="outside-the-app">Outside the app</h2><p><strong>44. Links that open notes.</strong> Every note has an address that starts with <code>obsidian://</code>. Run "Copy Obsidian URL for current file" and paste it anywhere: a task manager, a calendar event, an email to yourself. Clicking it opens the note. The same scheme can create notes too, with <code>obsidian://new</code>, and search, with <code>obsidian://search</code>.</p><p><strong>45. A command line, since 1.12.</strong> On desktop, turn on Settings, General, Command line interface. With Obsidian running, <code>obsidian daily</code> opens today's note from a terminal, and <code>obsidian search query="meeting notes"</code> searches the vault. Scripts and automations can talk to your vault without any plugin.</p><p><strong>46. Clip the web.</strong> The Obsidian Web Clipper is the official browser extension, free and open source, for Chrome-based browsers, Firefox and Safari. It saves pages as clean Markdown, keeps your highlights and lets you make templates per site, with variables like <code>{{title}}</code>, <code>{{url}}</code> and <code>{{highlights}}</code>.</p><h2 id="safety-nets-and-settings">Safety nets and settings</h2><p><strong>47. File recovery has your back.</strong> This core plugin quietly takes snapshots of your notes, by default every five minutes and kept for seven days. When you delete half a note by accident, go to Settings, File recovery, Snapshots, View, pick the note and restore an older version. The snapshots stay on the device and are not a real backup, so keep <a href="https://pablomurad.com/backups-3-2-1-restic-rclone/">a proper one</a> anyway.</p><p><strong>48. Hide the noise.</strong> Settings, Files and links, Advanced, Excluded files. Anything you list there, like an archive of imports or a folder of attachments, disappears from search, the graph and unlinked mentions, and shows up less in the quick switcher and in link suggestions.</p><p><strong>49. Different settings per device.</strong> If you sync your vault with something like Syncthing, every device shares the same <code>.obsidian</code> folder, plugins and all. Settings, Files and links, "Override config folder" lets one device use its own, with any name that starts with a dot, like <code>.obsidian-phone</code>. The new folder starts empty, so you set that device up once. After that, your laptop and your phone can have different themes, hotkeys and layouts in the same vault.</p><p><strong>50. Make the phone yours.</strong> On mobile, Settings, Interface has two settings worth a visit. "Configure mobile toolbar" decides which buttons sit above the keyboard, and any command can be one of them. "Configure mobile Quick Action" sets what happens when you pull down on a note. By default it's the command palette, and it can be anything.</p><h2 id="start-with-three">Start with three</h2><p>Nobody needs fifty tricks at once. Pick three that solve something you already do by hand, use them for a week, then come back for three more. My suggestion for a first round: the search operators, the <code>query</code> block and a hotkey for moving lines.</p>]]></content>
    </entry>
    <entry>
        <title>Reading log, 2026-W45</title>
        <link href="https://pablomurad.com/reading-log-2026-w45/"/>
        <id>https://pablomurad.com/reading-log-2026-w45/</id>
        <published>2026-11-08T20:07:05-03:00</published>
        <updated>2026-11-08T20:07:05-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="reading"/>
        <summary>The best of my read-later list. hax0r: News for hackers and programmers hax0r.zip so i guess everyone uses llms to think now - eva&apos;s site eva.ac If you love a website, back it up - The Future Is Now blog.mistys-internet.website We have lift off 🚀: building a gemini server in</summary>
        <content type="html"><![CDATA[<p>The best of my read-later list.</p><ul class="link-log"><li><a href="https://hax0r.zip/">hax0r: News for hackers and programmers</a> <span class="via">hax0r.zip</span><p class="summary">Daily links from Hacker News, Lobsters, SoylentNews, Tildes and Lemmy communities, what people are discussing next to what just came out.</p></li><li><a href="https://eva.ac/blog/llms/">so i guess everyone uses llms to think now - eva's site</a> <span class="via">eva.ac</span><p class="summary">so i guess everyone uses llms to think now</p></li><li><a href="https://blog.mistys-internet.website/post/2026/10/29/if-you-love-a-website-back-it-up/">If you love a website, back it up</a> <span class="via">blog.mistys-internet.website</span><p class="summary">This story starts with a little tragedy. See, I’ve got these old webpages I love to go back and look on every now and then. There’s websites that h...</p></li><li><a href="https://www.willmunn.xyz/gemini/python/2026/10/16/we-have-lift-off.html">We have lift off 🚀: building a gemini server in python</a> <span class="via">willmunn.xyz</span><p class="summary">7 years after its invention, I discovered the Gemini protocol. It’s a kind of “back to first principals” alternative to the web, aimed at providing value to independent content creators away from the noise, bots and trackers of the main web.</p></li><li><a href="https://www.reddit.com/r/linuxbrasil/comments/1xnm7vp/transformei_meu_linux_no_templeos/">Transformei meu Linux no TempleOS : r/linuxbrasil</a> <span class="via">reddit.com</span></li><li><a href="http://www.catb.org/jargon/html/story-of-mel.html">The Story of Mel</a> <span class="via">catb.org</span></li><li><a href="https://widdershins.verja.net/a-smaller-internet-can-still-be-an-open-one/">A Smaller Internet Can Still Be an Open One</a> <span class="via">widdershins.verja.net</span><p class="summary">I read Ben Thompson’s “The End of the Internet”, and I agree with his preference for smaller communities. Where he loses me is the suggestion that the public...</p></li><li><a href="https://nicholas.carlini.com/writing/2026/algorithmic-improvements-chess.html">Could a modern chess engine on a Pentium have defeated Kasparov in 1996?</a> <span class="via">nicholas.carlini.com</span><p class="summary">A fun experiment where I back-port all the improvements we've made to chess engines over the last 30 years and run it on a 1996 Pentium Pro to see what we could have done if only we knew more at the time.</p></li><li><a href="https://blog.veitheller.de/A_Second_Futamura_Projection.html">A Second Futamura Projection</a> <span class="via">blog.veitheller.de</span><p class="summary">Two months ago, we turned compfuck back into an interpreter and wrote a specializer that, given a Brainfuck program, folded the interpreter away and left…</p></li><li><a href="https://lwn.net/Articles/446528/">On vsyscalls and the vDSO</a> <span class="via">lwn.net</span><p class="summary">The 'vsyscall' and 'vDSO' segments are two mechanisms used to accelerate certain system calls i [...]</p></li><li><a href="https://sshhole.com/">*sshole · sshhole.com</a> <span class="via">sshhole.com</span><p class="summary">openssh on musl linux has been letting people into password-less accounts since 2016.</p></li><li><a href="https://lists.debian.org/debian-security-announce/2026/msg00526.html?pow_referer=">[SECURITY] [DSA 6607-1] apt security update</a> <span class="via">lists.debian.org</span></li><li><a href="https://blog.cryptographyengineering.com/2015/07/20/a-history-of-backdoors/">A history of backdoors</a> <span class="via">blog.cryptographyengineering.com</span><p class="summary">The past several months have seen an almost eerie re-awakening of the ‘exceptional access’ debate — also known as ‘Crypto Wars’. For those just joining the deba…</p></li><li><a href="https://javahippie.net/programming/llm/2026/03/05/the-lisp-curse-coming-to-your-ecosystem.html">The LISP curse, now coming to your ecosystem! - Tim Zöller</a> <span class="via">javahippie.net</span><p class="summary">The LISP curse is often quoted as the reason why so few libraries and reusable components exist for LISP dialects. According to the concept, LISP is so power...</p></li><li><a href="https://mcp-explained.com/">MCP Explained: how the Model Context Protocol works</a> <span class="via">mcp-explained.com</span><p class="summary">Every MCP message on the wire, step by step. Transports, auth, tools, sampling, tasks, tool search and codemode. By Runlayer.</p></li><li><a href="https://criminallyvulgar.dev/you-should-absolutely-download-your-favourite-youtube-videos/">You should absolutely download your favourite youtube videos</a> <span class="via">criminallyvulgar.dev</span><p class="summary">Signal boosting Laura's post that you should absolutely download and archive the stuff that matters to you.</p></li><li><a href="https://blog.arusekk.pl/posts/srht-disabled-registration-bypass/">SourceHut disabled registration bypass</a> <span class="via">blog.arusekk.pl</span><p class="summary">You could register accounts on SourceHut instances who disabled registration</p></li><li><a href="https://noteflakes.com/articles/2026-11-04-datastar">A Look at Datastar - Noteflakes</a> <span class="via">noteflakes.com</span><p class="summary">I have always been interested in frontend solutions for building web applications. Even though I'm more of a backend programmer, I really dig all the different techniques people come up with...</p></li><li><a href="https://onlinegoddess.net/2026/11/the-cost-of-blogging/">The Cost of Blogging</a> <span class="via">onlinegoddess.net</span><p class="summary">It’s easy enough to find a blogging platform that doesn’t cost anything to use, Bear itself has a decent enough free tier, but how many of us do end up spend...</p></li><li><a href="https://www.karthihegde.dev/posts/dns_tampering">So my ISP was hijacking my DNS</a> <span class="via">karthihegde.dev</span><p class="summary">My ISP was tampering with my DNS queries and handing back bogus IP addresses. Here are the misconceptions about DoH, DoT and DNSSEC that followed, and the setup I actually run.</p></li></ul>]]></content>
    </entry>
    <entry>
        <title>Cock.li, email the way it used to be</title>
        <link href="https://pablomurad.com/cock-li-email-the-way-it-used-to-be/"/>
        <id>https://pablomurad.com/cock-li-email-the-way-it-used-to-be/</id>
        <published>2026-11-08T18:58:29-03:00</published>
        <updated>2026-11-08T18:58:29-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="email"/>
        <summary>Cock.li gives you an email address the way it used to be: a name, a password and a captcha. IMAP, POP, XMPP, Tor addresses for everything, and Cock-mail, a whole webmail in a single HTML file that runs in your browser. Free since 2013.</summary>
        <content type="html"><![CDATA[<p><em>This post isn't sponsored, and I'm not affiliated with cock.li in any way. I write about anything I find interesting.</em></p><p>Remember when getting an email address took a minute? You picked a name, typed a password twice and that was it. No phone number, no "verify it's you", no second email to recover the first one. <a href="https://cock.li">Cock.li</a> still works like that, and I loved it.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/ftlwxyh6rqpryp8897lxbqtezl55/cock-li-home.png" alt="The cock.li home page, a plain page with a dark brown border. At the top, a black and white cartoon character in a cap with the word &quot;COCK&quot; on it, next to the title &quot;cock.li - yeah it's mail with cocks&quot; and a row of links: Home, Webmail, Donate, Contact, Unblock SMTP, Change Password and Register. The About section says the host has been mostly alive since 2013, never asks for personal information, and counts more than a million users. On the right, a donations bar labeled &quot;The bar's just here for looks&quot; and the server info: mail.cock.li for IMAP and POP, xmpp.cock.li for XMPP, and two .onion addresses." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/NgC3P8tPsO-480.webp 480w, https://pablomurad.com/img/NgC3P8tPsO-800.webp 800w, https://pablomurad.com/img/NgC3P8tPsO-1200.webp 1200w, https://pablomurad.com/img/NgC3P8tPsO-1600.webp 1600w, https://pablomurad.com/img/NgC3P8tPsO-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>It's a free email host that has been around since 2013 and has more than a million users. The tagline on the front page is "yeah it's mail with cocks", which tells you most of what you need to know about its sense of humor. The rest is on the same page: you will never be asked to verify personal information, because the people behind it believe anyone should have access to email without tying it to the surveillance state.</p><h2 id="signing-up-takes-a-minute">Signing up takes a minute</h2><p>The registration page asks for three things: a username, a password and a captcha. That's it. You also choose your domain from a list, and this is where the fun starts. Besides cock.li itself there's airmail.cc, firemail.cc, memeware.net, cocaine.ninja, waifu.club, 420blaze.it, cock.email and several others, some of them a lot ruder. Picking one is half the experience.</p><p>Once you're in, new accounts need to unlock sending. Instead of asking for a phone number, cock.li asks your browser to solve a small proof of work, a few minutes of CPU time, and the page that does it is titled, of course, "unblock your cock". It's a clever way to keep spammers out without collecting anything about you.</p><h2 id="email-and-a-webmail-in-a-single-file">Email, and a webmail in a single file</h2><p>You get IMAP and POP at <code>mail.cock.li</code>, so any mail client works: Thunderbird, Claws Mail, Apple Mail, K-9 on Android, Mutt in a terminal.</p><p>And then there's Cock-mail, the part that impressed me the most. It's their webmail, written from scratch in 2026, and the whole thing is a single HTML file. You can open it with view-source and read every line of it. That one file runs entirely in your browser and talks directly to the IMAP and SMTP servers over WebSockets. There's no webmail backend in the middle logging in for you and juggling your session. The browser speaks the mail protocols itself, the same way a desktop client would. The developers say it plainly: they believe the best email client is a desktop client, and they built a webmail that behaves like one.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/362z0df2dx5c6rfvxlksiwubznyl/cock-mail-compose.png" alt="Cock-mail, the cock.li webmail, in a dark theme. A sidebar on the left lists the mailboxes (Inbox with 5 unread, Trash, Sent and a few folders) and links for Compose, Settings, Disconnect and View Logs. The middle column is the inbox, with test messages such as &quot;Inline image&quot;, &quot;Attached HTML e-mail&quot; and &quot;Plaintext E-mail&quot;. On the right, a reply is being written in plain text with From, To, CC, BCC and Subject fields, quoting an older message, with a text file attached." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/cSgm9Z1297-480.webp 480w, https://pablomurad.com/img/cSgm9Z1297-800.webp 800w, https://pablomurad.com/img/cSgm9Z1297-1200.webp 1200w, https://pablomurad.com/img/cSgm9Z1297-1600.webp 1600w, https://pablomurad.com/img/cSgm9Z1297-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>A few things I really like about it:</p><ul>
<li value="1">HTML emails are turned into plain text, with the links pulled out and listed. Remote images are never loaded. No tracking pixels, no surprises.</li>
<li value="2">It shows live unread counts and updates by itself when something changes from another client.</li>
<li value="3">It handles attachments, inline images, emails attached inside other emails, every character set, and opens mailboxes with tens of thousands of messages.</li>
<li value="4">It works on a phone too, with a portrait layout.</li>
<li value="5">It's free to use and to deploy on your own mail server, under a permissive license. If your server already speaks WebSockets, that single file is all you need: you set your endpoints at the bottom of it and you're done. The full package, with the server-side helper, is a 60 KB download.</li>
</ul><p>The changelog reads like a craftsman's notebook, with entries like "real IMAP protocol parsing" and support for a mailbox named with a pile of backslashes and quotes, just because it should work. Filters are planned for version 1.0.</p><h2 id="more-than-email">More than email</h2><p>The same account also gives you XMPP, at <code>xmpp.cock.li</code>, so you can chat with any Jabber user in the world with the address you already have.</p><p>Everything is also reachable through Tor. There's a <code>.onion</code> address for the website and another one for the webmail, IMAP, POP and XMPP, so you can use the whole service without ever leaving the Tor network.</p><h2 id="small-things-that-show-care">Small things that show care</h2><p>The site keeps a plain text log at <code>cock.li/log.txt</code> with every update since 2024, written in a voice that's half status report and half stand-up comedy. It suggests you download it and use <code>diff</code> to see what changed, which is refreshingly honest advice for a changelog. There's a warrant canary, a transparency page and GPG keys for the team, signed by an offline master key.</p><p>They own their hardware and say they have no third parties. The privacy policy is written in plain language and says exactly what is kept and for how long, with mail logs cleared after a couple of days.</p><p>And it's run by a small team, mostly as volunteer work, kept alive by donations in Monero and Bitcoin. The donation bar on the front page carries a perfect label: "The bar's just here for looks."</p><h2 id="go-get-an-address">Go get an address</h2><p>If you miss the internet where an email address was something you just had, try it. Pick the domain that makes you laugh, set up a client or open Cock-mail, and if you end up liking it as much as I did, <a href="https://cock.li/donate.php">send them a donation</a>. Services like this are rare, and they run on people who care.</p>]]></content>
    </entry>
    <entry>
        <title>Bloggram turns your Telegram channel into a blog</title>
        <link href="https://pablomurad.com/bloggram-telegram-channel-blog/"/>
        <id>https://pablomurad.com/bloggram-telegram-channel-blog/</id>
        <published>2026-11-08T04:56:20-03:00</published>
        <updated>2026-11-08T04:56:20-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="projects"/>
        <summary>Bloggram turns a Telegram channel into a blog search engines can find: every post gets its own page, title and description, with tag pages, resized photos, RSS and a sitemap. Edits and deletions follow along. Free, no ads, no JavaScript.</summary>
        <content type="html"><![CDATA[<p>A lot of good writing lives on Telegram. People run channels about cooking, local news, programming, books, their neighborhood, their dog. They post every day, people read every day, and almost none of it can be found outside the app. Search for a recipe you know is sitting in some channel and you probably won't find it. Telegram has a public preview at <code>t.me/s/</code>, but it has no readable addresses, no titles, no tags and no sitemap, so search engines don't do much with it.</p><p><a href="https://bloggram.lol">Bloggram</a> fixes that. It takes a Telegram channel and turns it into a blog that search engines can read, and it keeps that blog in sync with the channel on its own. You keep writing where you already write.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/lr4gtxcsjdxtypd4y45x36pkp8fi/bloggram-home.png" alt="The Bloggram home page, plain black text on white in a serif font, with no images. The headline reads &quot;Your Telegram channel, as a blog that search engines can find.&quot; Below it, a short paragraph explains that every post becomes a web page with its own address, title and description, a &quot;Start your blog&quot; link, a note that Bloggram is free, has no ads or trackers and runs without JavaScript, and a list titled &quot;What you get&quot; with an address like yourname.bloggram.lol, tag pages, resized photos, RSS and a sitemap." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/pCriZc5dwL-480.webp 480w, https://pablomurad.com/img/pCriZc5dwL-800.webp 800w, https://pablomurad.com/img/pCriZc5dwL-1200.webp 1200w, https://pablomurad.com/img/pCriZc5dwL-1600.webp 1600w, https://pablomurad.com/img/pCriZc5dwL-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="three-steps-and-the-third-one-is-automatic">Three steps, and the third one is automatic</h2><p>First, you sign in with Telegram. There's no password to create: you send a message to the bot, <a href="https://t.me/bloggygram_bot">@bloggygram_bot</a>, and it gives you a button that opens your dashboard.</p><p>Then you add that bot to your channel as an administrator, with two permissions switched on: post messages and edit messages. It has to be a channel. Groups don't work.</p><p>That's it. The moment the bot becomes an admin, the blog exists. Its address comes from the channel's name, so a channel called "Café Notes" becomes <code>cafe-notes.bloggram.lol</code>. If the channel is public, its older posts come along too, so the blog doesn't start empty. A private channel starts from its next post.</p><h2 id="what-a-post-becomes">What a post becomes</h2><p>Every post gets its own page, with an address like <code>/p/123-your-title</code>. The first line of the post becomes its title, so short first lines make good titles. The start of the text becomes the description that shows up under the link in search results and in previews when someone shares it.</p><p>The formatting you use in Telegram comes along: bold, italic, underline, strikethrough, code, quotes and links. Even spoilers work, staying hidden until the reader hovers over them.</p><p>Hashtags turn into tag pages. Write <code>#travel</code> in a few posts and <code>/tag/travel</code> lists all of them, newest first.</p><p>Photos are copied to Bloggram and stored in up to three sizes, so a phone downloads a small file and a big screen gets a sharp one. Albums become a single post with every photo in the order you sent them, and the first photo is the image people see when the link is shared. Video, audio and files stay on Telegram. The post still goes up, with a short note saying there's media to see in the channel.</p><h2 id="when-you-change-your-mind">When you change your mind</h2><p>Edit a post in Telegram and the page changes within seconds.</p><p>Deleting is the funny part. Telegram never tells a bot when a channel post is deleted, so Bloggram has to go and check. It looks at recent posts every few minutes and at older ones less often, and when a post is gone from the channel, its page and its photos come off the blog. For private channels it checks with an empty edit, which is why the bot needs the edit permission.</p><p>If you want a post to stay in the channel but not on the blog, you can hide it from the dashboard. The dashboard also lets you change the blog's title, description, language and address, import the history again or delete the blog.</p><h2 id="built-to-be-found">Built to be found</h2><p>The whole point is search, so every blog gets the boring, important things done right: a proper title and description on every page, canonical addresses, link previews for social networks, structured data that marks each post as a blog post with its date and language, a sitemap, a <code>robots.txt</code> that points to it, and an RSS feed at <code>/rss.xml</code> with the last 50 posts in full.</p><p>There's also a rule against spam. Anyone can open a Telegram channel in a minute, and a free blog service is a gift to spammers. So a new blog stays out of search engines until it looks like a real one: at least 5 posts, 10 subscribers in the channel and 3 days of life. Until then, people can read it and follow its feed normally, and only search engines are asked to wait. Bloggram reviews new blogs once a day, and the dashboard shows what's still missing. Links to other sites are marked <code>nofollow</code>, so nobody gets anything out of spamming them.</p><h2 id="small-on-purpose">Small on purpose</h2><p>Bloggram is one program and one database file. There's no separate database server, no queue service and no cache server. Pages are kept in memory and usually come out in a couple of milliseconds, and the blogs themselves run without a single line of JavaScript. The whole thing runs on a single server.</p><p>For you, that means pages that load fast, even on a bad phone connection. It's free, with no ads and no trackers, and the only account you need is the Telegram one you already have.</p><p>There are a few limits, mostly to keep things healthy: three blogs per account, photos up to 20 MB and a cap on how many new blogs can open each day. Your own domain isn't there yet, but it's planned. And if you ever want out, you can delete a blog from the dashboard, or delete your whole account by sending <code>/deleteaccount</code> to the bot.</p><h2 id="try-it">Try it</h2><p>Bloggram is brand new, still at version 0.01, and it's the kind of thing that gets better with real channels using it. If you run a Telegram channel, or know someone who does, <a href="https://app.bloggram.lol/">start a blog</a> and see your posts become pages. The <a href="https://bloggram.lol/docs">documentation</a> explains every detail, from how titles are chosen to what happens to your photos.</p><p>And if something breaks or feels wrong, write to me at <a href="mailto:pablomurad@pm.me">pablomurad@pm.me</a>.</p>]]></content>
    </entry>
    <entry>
        <title>Emacs beyond the basics, part I: feeds, gopher, the fediverse and mail</title>
        <link href="https://pablomurad.com/emacs-beyond-the-basics-part-1/"/>
        <id>https://pablomurad.com/emacs-beyond-the-basics-part-1/</id>
        <published>2026-11-08T02:49:00-03:00</published>
        <updated>2026-11-08T02:57:48-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="emacs"/>
        <summary>Emacs as a small operating system: read RSS with elfeed, browse gopher and Gemini with Elpher, follow the fediverse with mastodon.el, chat on IRC and Matrix, read the web in EWW and send mail. Keys, configs and a starter block.</summary>
        <content type="html"><![CDATA[<p>The <a href="https://pablomurad.com/emacs-for-the-curious/">first Emacs guide</a> on this blog repeated an old joke: Emacs is a great operating system lacking a decent text editor. This post takes the joke seriously. Emacs can read your feeds, browse gopher and Gemini, talk to the fediverse, sit in IRC rooms, browse the web and handle your email, all with the same keys you already learned for editing text.</p><p>You don't have to move your whole life into it. The fun is picking one thing, trying it for a week and seeing if it sticks. Each section below works on its own.</p><h2 id="getting-packages">Getting packages</h2><p>Some of what follows is built into Emacs, and some comes as packages. Emacs already knows two package archives out of the box, GNU ELPA and NonGNU ELPA. The config from the first guide also adds MELPA, the biggest community archive. With those three, every package in this post is one <code>use-package</code> block away.</p><h2 id="feeds-with-elfeed">Feeds with elfeed</h2><p>If you follow blogs, news sites, YouTube channels or forums through RSS, elfeed turns Emacs into a fast, keyboard-driven feed reader. It's on NonGNU ELPA, so Emacs can install it without any extra setup.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> elfeed</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c w</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> elfeed</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">custom</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">elfeed-feeds</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">https://planet.emacslife.com/atom.xml</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> emacs</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">https://example.com/feed.xml</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> blogs friends</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> video</span><span style="color:#666666">))))</span></span></code></pre></div><p>Each feed is a list: the address first, then any tags you want to give it. Tags are how you'll slice your reading later. If you already have your subscriptions in another reader, export them as OPML and bring them in with <code>M-x elfeed-load-opml</code>.</p><p>Press <code>C-c w</code> to open elfeed, then <code>G</code> to fetch every feed. The main screen is a list of entries, newest first:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>RET: read the entry</span></span>
<span class="line"><span>b: open the entry in your web browser</span></span>
<span class="line"><span>n / p: next / previous entry</span></span>
<span class="line"><span>r: mark as read</span></span>
<span class="line"><span>u: mark as unread</span></span>
<span class="line"><span>+ / -: add or remove a tag</span></span>
<span class="line"><span>s: filter the list as you type</span></span>
<span class="line"><span>c: clear the filter</span></span>
<span class="line"><span>G: fetch new entries</span></span>
<span class="line"><span>q: quit</span></span></code></pre></div><p>Inside an entry, <code>n</code> and <code>p</code> jump to the next and previous one, <code>b</code> opens it in the browser and <code>q</code> goes back to the list.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/4tm8g19rw13dkgda1ap2jsf9uatr/elfeed-screenshot.png" alt="elfeed in a dark Emacs window: a long list of feed entries, each with a date, a title, the name of the feed and its tags, such as blog, emacs, youtube, comic and unread, with the unread entries in bold. The filter line at the bottom reads &quot;-junk @22-hours-ago&quot;." width="742" height="655" loading="lazy" srcset="https://pablomurad.com/img/Zq4gH26gUV-480.webp 480w, https://pablomurad.com/img/Zq4gH26gUV-742.webp 742w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The filter is the clever part. The default shows unread entries from the last six months, and you can write your own with a small language:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>+unread: only unread entries</span></span>
<span class="line"><span>+emacs: only entries tagged emacs</span></span>
<span class="line"><span>-video: hide entries tagged video</span></span>
<span class="line"><span>@2-weeks-ago: only entries from the last two weeks</span></span>
<span class="line"><span>=example.com: only entries from feeds that match this text</span></span></code></pre></div><p>Mix them as you like. <code>@1-week-ago +unread -video</code> shows what you haven't read this week, minus the videos. Press <code>s</code>, type the filter, and the list updates while you type.</p><p>Emacs also ships a feed reader of its own, Newsticker (<code>M-x newsticker-treeview</code>), if you'd rather not install anything. Elfeed is faster and nicer to filter, but it's good to know the built-in one is there.</p><h2 id="gopher-and-gemini-with-elpher">Gopher and Gemini with Elpher</h2><p>Gopher is the protocol that almost became the web, back in the early 90s. Gemini is its modern cousin, a small protocol made for plain text pages without tracking or scripts. Both are alive and full of personal sites, and Elpher, from NonGNU ELPA, browses both.</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> elpher</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span></code></pre></div><p><code>M-x elpher</code> opens a start page with your bookmarks. To visit somewhere, press <code>g</code> and type an address:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>gopher://sdf.org</span></span>
<span class="line"><span>gemini://geminiprotocol.net</span></span></code></pre></div><p>From there, it's a keyboard browser:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>TAB: jump to the next link</span></span>
<span class="line"><span>RET: follow the link under the cursor</span></span>
<span class="line"><span>u: go back</span></span>
<span class="line"><span>g: go to an address</span></span>
<span class="line"><span>a: bookmark the link under the cursor</span></span>
<span class="line"><span>A: bookmark the page you're on</span></span>
<span class="line"><span>B: show your bookmarks</span></span>
<span class="line"><span>d: download the link under the cursor</span></span>
<span class="line"><span>.: view the raw source of the page</span></span>
<span class="line"><span>R: reload</span></span></code></pre></div><p>If you read the <a href="https://pablomurad.com/sdf-guide-part-1/">SDF guide</a>, this is the comfortable way to read the gopherholes of SDF members without leaving your editor.</p><h2 id="the-fediverse-with-mastodon-el">The fediverse with mastodon.el</h2><p>mastodon.el, from NonGNU ELPA, is a full client for Mastodon. It also works with other servers that speak the same API, like GoToSocial, Pleroma and Akkoma. Tell it your server and your username:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> mastodon</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">custom</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">mastodon-instance-url </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">https://your.server</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">mastodon-active-user </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">yourname</span><span style="color:#C98A7D77">"</span><span style="color:#666666">))</span></span></code></pre></div><p>Run <code>M-x mastodon</code>. The first time, it opens your browser so you can authorize the app on your server, then asks you to paste back the code the server shows you. It keeps the login in your auth-source file (usually <code>~/.authinfo.gpg</code>), so you only do this once. After that, you land on your home timeline.</p><p>The keys are short and easy to remember:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>H: home timeline</span></span>
<span class="line"><span>L: local timeline</span></span>
<span class="line"><span>F: federated timeline</span></span>
<span class="line"><span>N: notifications</span></span>
<span class="line"><span>n / p: next / previous post</span></span>
<span class="line"><span>t: write a new post</span></span>
<span class="line"><span>r: reply</span></span>
<span class="line"><span>b: boost</span></span>
<span class="line"><span>f: favourite</span></span>
<span class="line"><span>k: bookmark</span></span>
<span class="line"><span>T: open the whole thread</span></span>
<span class="line"><span>o: open the post in the browser</span></span>
<span class="line"><span>s: search</span></span>
<span class="line"><span>O: your profile</span></span>
<span class="line"><span>g: refresh</span></span>
<span class="line"><span>Q: close mastodon.el</span></span></code></pre></div><p>Writing a post opens a small buffer. Type, then <code>C-c C-c</code> sends it. Since it's an Emacs buffer, everything you know works there, including the spell checker (<code>M-x flyspell-mode</code>).</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/ve0ocnvnpk3qkkcoq69rldv5rlx3/mastodon-el-compose.png" alt="mastodon.el in a dark Emacs window, split in two. The top half shows notifications: a favourite on a post tagged #emacs #forgejo #codeberg, and a mention from another account. The bottom half is the compose buffer, with a list of keys such as C-c C-c to send, C-c C-a to attach media and C-c C-w to add a content warning, a character counter, and a post being typed while a list of matching accounts pops up to complete a handle." width="939" height="1080" loading="lazy" srcset="https://pablomurad.com/img/d3RGqqgLrH-480.webp 480w, https://pablomurad.com/img/d3RGqqgLrH-800.webp 800w, https://pablomurad.com/img/d3RGqqgLrH-939.webp 939w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="chat-irc-and-matrix">Chat: IRC and Matrix</h2><p>ERC is an IRC client that has been part of Emacs for a long time. Run <code>M-x erc-tls</code>, answer the questions about server, port and nickname, and you're connected. Inside, it's IRC as usual: <code>/join #channel</code>, <code>/msg someone hello</code>, <code>/part</code> to leave.</p><p>To join the same rooms every time, list them per server:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> erc-autojoin-channels-alist</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">libera.chat</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">#emacs</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span></code></pre></div><p>Libera.Chat, where a lot of free software projects live, has an <code>#emacs</code> channel full of patient people. Many public Unix systems and pubnixes also run their own IRC servers, and ERC talks to all of them. If your server asks for a SASL login, ERC has a <code>sasl</code> module: add it to <code>erc-modules</code> and use your NickServ password.</p><p>For Matrix, there's Ement.el, from GNU ELPA. <code>M-x ement-connect</code> asks for your Matrix ID and password, and then lists your rooms.</p><h2 id="the-web-with-eww">The web with EWW</h2><p>EWW is a web browser built into Emacs. It doesn't run JavaScript and it shows pages as text with images, which sounds limiting until you realize how many pages read better that way. Documentation, blogs, Wikipedia and most of the small web look great in it.</p><p><code>M-x eww</code> asks for an address or a search term. Then:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>l / r: back / forward</span></span>
<span class="line"><span>g: reload</span></span>
<span class="line"><span>R: readable mode, which strips everything but the main text</span></span>
<span class="line"><span>&amp;: open this page in your normal browser</span></span>
<span class="line"><span>w: copy the address</span></span>
<span class="line"><span>b: bookmark this page</span></span>
<span class="line"><span>B: list bookmarks</span></span>
<span class="line"><span>d: download the link under the cursor</span></span>
<span class="line"><span>v: view the page source</span></span>
<span class="line"><span>G: go somewhere else</span></span></code></pre></div><p><code>R</code> is the one that makes EWW worth it. It throws away menus, sidebars and banners and leaves only the article. And <code>&amp;</code> is the escape hatch: when a page really needs a full browser, one key sends it there.</p><h2 id="mail">Mail</h2><p>Email is the deepest rabbit hole here, so this section is a map more than a manual. There are three common paths.</p><p>Gnus comes with Emacs. It started as a newsreader for Usenet and grew into a mail client. It's powerful and has its own way of thinking, which some people love and others don't.</p><p>mu4e and notmuch both work with mail stored on your disk. A separate program, usually <code>mbsync</code> from the isync project, downloads your mail from the server into a local folder. Then <code>mu</code> or <code>notmuch</code> indexes it, and Emacs gives you fast search, threads and tags on top. Both are installed with your system's package manager, and their manuals explain the setup step by step.</p><p>Sending mail, on the other hand, is simple and built in. Tell Emacs who you are and which server to use:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> user-full-name </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">Your Name</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#B8A965">user-mail-address</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">you@example.com</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#DBD7CAEE">send-mail-function </span><span style="color:#666666">'</span><span style="color:#C99076">smtpmail-send-it</span></span>
<span class="line"><span style="color:#DBD7CAEE">smtpmail-smtp-server </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">smtp.example.com</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#DBD7CAEE">smtpmail-smtp-service </span><span style="color:#4C9A91">587</span></span>
<span class="line"><span style="color:#DBD7CAEE">smtpmail-stream-type </span><span style="color:#666666">'</span><span style="color:#C99076">starttls</span><span style="color:#666666">)</span></span></code></pre></div><p>Put your password in a file called <code>~/.authinfo.gpg</code>, one line per server:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>machine smtp.example.com login you@example.com password your-password port 587</span></span></code></pre></div><p>The <code>.gpg</code> at the end makes Emacs encrypt the file with GnuPG when you save it, and decrypt it when it needs the password. Your password never sits in plain text in your config.</p><p>Now <code>C-x m</code> opens a new message. Fill in the To and Subject lines, write, and <code>C-c C-c</code> sends it.</p><h2 id="small-things-worth-knowing">Small things worth knowing</h2><p>A few more built-in tools that make Emacs feel like a small desktop:</p><ul>
<li value="1">
<code>M-x calendar</code> shows a calendar. Move with the arrows, <code>.</code> jumps to today, and <code>M-x diary</code> keeps simple appointments in a text file.</li>
<li value="2">
<code>M-x dictionary</code> looks up words in online dictionaries.</li>
<li value="3">
<code>M-x proced</code> lists running processes, like <code>top</code>, and lets you kill them with a key.</li>
<li value="4">
<code>M-x webjump</code> opens favorite sites by short names.</li>
<li value="5">
<code>M-x eshell</code> is a shell that works the same on Linux and Windows.</li>
<li value="6">
<code>M-x doctor</code> is a psychotherapist from the 1960s. It won't help, but it will listen.</li>
</ul><h2 id="if-you-re-on-emacs-31">If you're on Emacs 31</h2><p>Two changes in Emacs 31 make this way of living a bit nicer. In a terminal, the mouse now works by default in compatible terminals, so <code>emacs -nw</code> over SSH lets you click links in elfeed or scroll a timeline. And there's a new theme called <code>newcomers-presets</code>, off by default, that turns on a set of friendlier settings for people new to Emacs. Enable it like any other theme, with <code>M-x load-theme</code>.</p><h2 id="a-starter-block">A starter block</h2><p>Here are the packages from this post together, to paste after the config from the first guide:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#758575DD">;;; Living inside Emacs</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> elfeed</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">bind</span><span style="color:#666666"> (</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">C-c w</span><span style="color:#C98A7D77">"</span><span style="color:#CB7676"> .</span><span style="color:#DBD7CAEE"> elfeed</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">custom</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">elfeed-feeds</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">https://planet.emacslife.com/atom.xml</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> emacs</span><span style="color:#666666">))))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> elpher</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> mastodon</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">custom</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">mastodon-instance-url </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">https://your.server</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">mastodon-active-user </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">yourname</span><span style="color:#C98A7D77">"</span><span style="color:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">use-package</span><span style="color:#DBD7CAEE"> ement</span></span>
<span class="line"><span style="color:#666666">:</span><span style="color:#C99076">ensure</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> erc-autojoin-channels-alist</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">libera.chat</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">#emacs</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span></code></pre></div><p>Select it and press <code>TAB</code> to let Emacs indent it nicely. Then <code>M-x eval-buffer</code>, or just restart, and every tool in this post is a command away.</p>]]></content>
    </entry>
    <entry>
        <title>The small things I love</title>
        <link href="https://pablomurad.com/the-small-things-i-love/"/>
        <id>https://pablomurad.com/the-small-things-i-love/</id>
        <published>2026-11-07T22:54:38-03:00</published>
        <updated>2026-11-07T22:54:38-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="projects"/>
        <summary>Eight small things I built and love: bimb.ooo for pastes and tiny sites, tuno.place notes, pupu.page for writing, tedy.science sums, phlog.space for wrecked photos, and three ravens that carry email: RavenMail forms, theraven.my and the new theraven.social. All free, no ads.</summary>
        <content type="html"><![CDATA[<p>Some of my favorite things on the internet are tiny: a page that does one job, a tool with a cat on it, a place where nobody counts your followers.</p><p>So I keep making them. This post is a walk through eight small things I built and love: five cozy places from the same house, and three ravens that carry email around. All of them are free. Like I wrote on theraven.my: "All my projects are free to use. And when they mature, they become free and open-source software."</p><h2 id="five-rooms-in-the-same-house">Five rooms in the same house</h2><p>The first five share a house called muradcorp. Each one has a single job and the same rule: no ads, no tracking, nothing you didn't ask for.</p><h3 id="bimb-ooo-somewhere-to-put-things">bimb.ooo, somewhere to put things</h3><p><a href="https://bimb.ooo">bimb.ooo</a> is a small home for text, guarded by a pink elephant. You can paste a snippet of code, write a page in Markdown or hand-roll a tiny site with an <code>index</code>, a <code>style.css</code> and an <code>about</code>. Everything lands at <code>you.bimb.ooo</code>, under a name you pick, at paths you can read out loud over the phone.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/pnwfa7avxq80mirpzgqaq2gmwqed/bimbooo-home.png" alt="The bimb.ooo home page on a warm cream background. A small pink elephant sits next to a speech bubble that says &quot;pull up a chair. stay a while?&quot;. The headline reads &quot;somewhere to put things.&quot; in pink, followed by &quot;paste a snippet. write a page in markdown. hand-roll a little site in html. it all lands on you.bimb.ooo, under a name you pick.&quot;, two rounded buttons, &quot;ask for a seat&quot; and &quot;i have one&quot;, and three pastel cards for pastes, pages and a small site." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/JN3lU3YgYd-480.webp 480w, https://pablomurad.com/img/JN3lU3YgYd-800.webp 800w, https://pablomurad.com/img/JN3lU3YgYd-1200.webp 1200w, https://pablomurad.com/img/JN3lU3YgYd-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>There's no build step and no folder to upload. Every site gets an Atom feed at <code>/feed.xml</code> on its own, so people can follow you from a feed reader without you doing anything. Under the hood it's one Go binary with SQLite behind it, and every person lives on their own subdomain, so one person's HTML can never touch someone else's.</p><p>It's invite only, in the nicest way: you <a href="https://bimb.ooo/request">ask for a seat</a>, and you can <a href="https://bimb.ooo/square">see who is already here</a> before you do.</p><h3 id="tuno-place-a-board-for-loose-notes">tuno.place, a board for loose notes</h3><p><a href="https://tuno.place">tuno.place</a> is a board of colorful note cards, with Tuno, a white cat holding a pencil, at the top. You write a note, then make it look like yours: rounded rectangle, square, circle, arch, pill or hexagon, a background you can shuffle, a string tied from one note to another. When a note is done, you can save it as a picture or print it.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/gdl33ghvmgnm3gldd6lt5ls8yz77/tuno-notes-board.png" alt="The Tuno Notes board on lined paper. A small white cat logo and the words &quot;tuno notes&quot; sit at the top, with links to backup, import, new note, loose text and download, and a pink banner saying the notes stay in this browser. The big headline reads &quot;a small place for your big ideas.&quot; with &quot;big ideas&quot; in pink. Two note cards stand below it: an orange one titled &quot;Start here&quot; that says notes live in this browser only and to back them up now and then, and a blue one titled &quot;Don't forget&quot; with the checklist items &quot;Bring the book&quot; and &quot;Water the plants&quot;." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/912LXDUXoe-480.webp 480w, https://pablomurad.com/img/912LXDUXoe-800.webp 800w, https://pablomurad.com/img/912LXDUXoe-1200.webp 1200w, https://pablomurad.com/img/912LXDUXoe-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The notes never leave your browser. There's no account and no server holding them, which also means backing them up is your job (there's a button for that). It's officially described as "delightfully buggy".</p><h3 id="pupu-page-a-quiet-place-to-write">pupu.page, a quiet place to write</h3><p><a href="https://pupu.page">pupu.page</a> is a room for writing one thing at a time. The first thing it asks is "how is the light where you are?", night or day, and then it gets out of the way. Pupu, a small black cat, sleeps through both.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/jjveuen6643qwj1jq56wdj70tt7o/pupu-page-welcome.png" alt="The first screen of pupu.page, almost entirely black. A small sleepy black cat sits near the top, and below it the question &quot;how is the light where you are?&quot; with two choices, a dark square labeled &quot;it is night&quot; and a white one labeled &quot;it is day&quot;. A faint line at the bottom says you can change this whenever you like, and that pupu sleeps through both." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/Hbyx6IfQB5-480.webp 480w, https://pablomurad.com/img/Hbyx6IfQB5-800.webp 800w, https://pablomurad.com/img/Hbyx6IfQB5-1200.webp 1200w, https://pablomurad.com/img/Hbyx6IfQB5-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>You write in Markdown, and the punctuation steps aside: the stars and pound signs hide on every line except the one you're on, so what you see already reads like finished writing. The line you're typing holds still while the page moves around it, and everything except the sentence you're in can fade out, so your eyes have nowhere else to go. When you're done, you export a <code>.md</code> file that's byte for byte what was on the screen.</p><p>It works offline, saves as you type, and makes no request to anywhere else. Even the fonts live on the site itself, so nobody learns that you sat down to write today. No account, no server, no AI.</p><h3 id="tedy-science-a-desk-for-everyday-sums">tedy.science, a desk for everyday sums</h3><p><a href="https://tedy.science">Tedy's Science</a> is a notepad that does math. You type lines the way you'd say them, and the margin answers. Ted, a ginger cat with his tongue out, supervises.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/27mh37t9hmpi15fudgsz9rli68ju/tedys-science-home.png" alt="The Tedy's Science welcome screen on a beige background. In the center, Ted, a fluffy ginger cartoon cat with his tongue out, above the title &quot;Tedy's Science&quot; and the line &quot;a desk for everyday sums. write a line, the margin answers, even when the line ends with ?&quot;. Below are links to bimb.ooo, tuno.place and pupu.page, a &quot;start summing&quot; button and a link to buy Ted a treat on Ko-fi." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/Z8xRjXpThO-480.webp 480w, https://pablomurad.com/img/Z8xRjXpThO-800.webp 800w, https://pablomurad.com/img/Z8xRjXpThO-1200.webp 1200w, https://pablomurad.com/img/Z8xRjXpThO-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Write <code>lunch 45 + uber 22</code> and the margin says 67. The words are just labels, so your sums stay readable. A few more things it understands:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>850 + 20%: 1020</span></span>
<span class="line"><span>15% of ? = 60: ? = 400</span></span>
<span class="line"><span>120 km / 2 hours: 60 km/h</span></span>
<span class="line"><span>today + 30 days: the date 30 days from now</span></span>
<span class="line"><span>1200 in 12x: twelve payments of 100</span></span>
<span class="line"><span>1/3 cup * 3: 1 cup</span></span></code></pre></div><p>The <code>?</code> is the neat part: put it anywhere in a line and Ted solves for it. There are variables, headings that split a sheet into sections with their own subtotals, units that refuse to add kilograms to hours, dates, fractions, a scientific calculator running the same engine, and a set of ready-made formulas to paste in.</p><p>Your sheets live in your browser. To share one, you send a link, and the whole sheet travels inside the part of the address after the <code>#</code>, which never reaches the server. And if Ted is earning his keep, you can <a href="https://ko-fi.com/pmurad">buy him a treat on Ko-fi</a>.</p><h3 id="phlog-space-a-photo-log-of-wrecked-photos">phlog.space, a photo log of wrecked photos</h3><p><a href="https://phlog.space">phlog.space</a> is a photo log where your pictures get wrecked on purpose. You post a JPEG or PNG, and on the way in it gets one of 36 effects, picked at random: crunchy JPEG, pixel sorting, VHS, CGA and VGA palettes, fax, thermal camera, Kodachrome, Super 8, silent film, cyanotype, a square 2012 phone-app filter and more. Then it's shrunk to 400 pixels or less, and the original isn't kept.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/6fqyqynolsdp5ndta7lmkuj6fsqd/phlog-space-photo.png" alt="A photo page on phlog.space, framed by a pink bubbly pattern and a strip of musical notes. The photo shows two dark green and purple tomatoes on an orange garden table, its colors crushed by one of the site's effects, with the caption &quot;tomatoes, backyard grown!&quot; by ~verite, &quot;seen 48 times&quot;, a comment from murad that says &quot;hooo ye&quot; and a footer with terms, rss, an 88x31 phlog.space button and &quot;developed by muradcorp&quot;." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/kYdQuI_dMv-480.webp 480w, https://pablomurad.com/img/kYdQuI_dMv-800.webp 800w, https://pablomurad.com/img/kYdQuI_dMv-1200.webp 1200w, https://pablomurad.com/img/kYdQuI_dMv-1600.webp 1600w, https://pablomurad.com/img/kYdQuI_dMv-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>If you'd rather post a photo as it is, you can turn the effects off in your settings. The rest is built like the old web: each member can leave one "nice" and one single-line comment per photo, sign other members' guestbooks and browse by month. Profiles live at <code>/~name</code> and come with a hit counter, and there's a random photo link, a member list, an RSS feed and an 88x31 button for your own site.</p><p>Under the hood it's plain PHP with SQLite, no framework and no JavaScript. New accounts wait for an admin to let them in, so register and give it a little time.</p><h2 id="three-ravens">Three ravens</h2><p>The other three are about the one protocol that everybody already has: email. No app to install, no new password, no feed. You write from the mail program you already use.</p><h3 id="theraven-email-contact-forms-delivered-by-raven">theraven.email, contact forms delivered by raven</h3><p><a href="https://theraven.email">RavenMail</a> gives you free contact forms for any website. Make a form, put it on your site, and every message lands in your inbox, with Reply-To set to the sender so you answer by pressing reply.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/cghhh2srrk3jfxcvvlkyibk2nvck/ravenmail-home.png" alt="The RavenMail home page in black and white, with a typewriter-style font. The headline says &quot;Contact forms, delivered by raven.&quot; Below it: &quot;Make a form, put it on any site, and every message lands in your inbox. It costs nothing and there is no paid plan hiding behind it.&quot; On the right, an ink drawing of a stack of ravens carrying envelopes, next to a round stamp that reads &quot;free forever&quot; and a sealed letter." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/dFRG4Q_aZ4-480.webp 480w, https://pablomurad.com/img/dFRG4Q_aZ4-800.webp 800w, https://pablomurad.com/img/dFRG4Q_aZ4-1200.webp 1200w, https://pablomurad.com/img/dFRG4Q_aZ4-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Each form works three ways at once: as a link you can put in a bio, as an embed with one script tag, or as the target of a plain HTML form with no JavaScript at all. You sign in with a link sent to your email, so there's no password to forget.</p><p>What you get, all of it free: up to 10 forms, custom fields, attachments, webhooks to JSON, Discord or Slack, your own colors for light and dark mode, and labels in English, Portuguese or Spanish. Spam gets stopped without sending your visitors to anyone else: a small proof of work solved in their browser, a hidden trap field and a few limits. There's no upgrade screen. It's funded by donations.</p><h3 id="theraven-my-a-town-of-letters">theraven.my, a town of letters</h3><p><a href="https://theraven.my">theraven.my</a> is a blog you write by email. Each resident has a house and a secret address. You send an email to that address, and it becomes a post. Attach photos, and they become the pictures. You can sign up, edit, delete and change your settings, all by email. The site is only for reading.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/5qu3t5huncgd6zfzqhntyibn27q3/theraven-my-square.png" alt="The front page of theraven.my, titled &quot;The square&quot;. An ink drawing at the top shows a tiny round town, little houses around a central tower, with ravens flying around it. Below: &quot;Email hello@theraven.email to move in&quot;, links to How it works, Photos and Knock on a random door, and the first post on the square, &quot;The Raven, by Edgar Allan Poe&quot;, by Pablo Murad, dated 5 Nov 2026." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/CtqBwBhx1A-480.webp 480w, https://pablomurad.com/img/CtqBwBhx1A-800.webp 800w, https://pablomurad.com/img/CtqBwBhx1A-1200.webp 1200w, https://pablomurad.com/img/CtqBwBhx1A-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Everybody's posts meet in the town square, and there's a button to knock on a random door. It's a nice place to send a poem, a photo from your walk or a "we arrived" to your family. The last thing I posted there was Poe's The Raven. To move in, email <a href="mailto:hello@theraven.email">hello@theraven.email</a>.</p><h3 id="theraven-social-good-company-delivered">theraven.social, good company delivered</h3><p><a href="https://theraven.social">theraven.social</a> is the newest raven. It's a small post office for people who write letters.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/0k9z3ku6vcgcedi3hacj0ajyfh8q/theraven-social-home.png" alt="The theraven.social home page in black and white. The headline reads &quot;Good company. Delivered.&quot; with the text &quot;Follow a few people and their letters come to your inbox together, on the day and at the hour you pick. Reply to one and only its author reads it.&quot; and a button, &quot;Join with an invitation&quot;. On the right, an ink drawing of ravens working behind a post office counter full of pigeonholes, with stamps that say &quot;posted by email&quot;, &quot;no feed, no likes&quot; and &quot;on your day&quot;." width="1280" height="860" loading="lazy" srcset="https://pablomurad.com/img/om_3dAgGUJ-480.webp 480w, https://pablomurad.com/img/om_3dAgGUJ-800.webp 800w, https://pablomurad.com/img/om_3dAgGUJ-1200.webp 1200w, https://pablomurad.com/img/om_3dAgGUJ-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>You follow a few people, and their new letters arrive together in a single email, daily, twice a week or weekly, at the hour you pick, in your time zone. Each delivery has an end. Every letter has its own reply address, so when you write back, only the author reads it, and your email address stays hidden. Nobody sees who follows whom.</p><p>Just as important is what it doesn't have: no endless feed, no likes or rankings, no public follower counts, no read receipts or tracking pixels, no ads, no recommendations tuned to keep you scrolling, no AI writing or summarizing letters, and no app to install. You can do all of it from your mail program. Independent servers can even federate with each other.</p><p>It's open by invitation, and the archive of public letters is still empty. The first one could be yours.</p><h2 id="why-small">Why small</h2><p>None of these is trying to be big. Each one does one thing, keeps your words and pictures where you put them and lets you leave whenever you want.</p><p>Try one, or all eight. And if you make something with them, write to me. A raven will find me.</p>]]></content>
    </entry>
    <entry>
        <title>How to get into the demoscene: a practical guide</title>
        <link href="https://pablomurad.com/how-to-get-into-the-demoscene/"/>
        <id>https://pablomurad.com/how-to-get-into-the-demoscene/</id>
        <published>2026-11-07T14:39:05-03:00</published>
        <updated>2026-11-07T14:39:05-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="demoscene"/>
        <summary>A practical follow-up to my demoscene post: how to watch demos, run the real thing in emulators, find the best prods on Pouët and Demozoo, start making your own with Shadertoy, TIC-80, trackers and sizecoding, and when to go to a party.</summary>
        <content type="html"><![CDATA[<p>My <a href="https://pablomurad.com/software-as-culture-the-demoscene/">post about the demoscene</a> got shared a lot, so this is the practical follow-up. That one was about why the scene matters. This one is about what to do next: how to watch demos, how to run them for real, where to find the good ones, how to start making your own and how to show up at a party.</p><p>You don't need to be a programmer for most of it. You don't need old hardware either. A browser and a bit of curiosity get you surprisingly far.</p><h2 id="a-small-dictionary-first">A small dictionary first</h2><p>The scene has its own slang, and the sites you're about to visit use it everywhere. These are the words worth knowing before you start:</p><ul>
<li value="1">prod: short for production. Any release counts: a demo, an intro, a music track, a picture.</li>
<li value="2">demo: a real-time audiovisual piece with no size limit, or a generous one.</li>
<li value="3">intro: a demo with a strict size limit. The common ones are 64k (65,536 bytes) and 4k (4,096 bytes), and there are smaller categories like 1k, 256b and even less.</li>
<li value="4">party: a demoparty, the event where people meet and release prods.</li>
<li value="5">compo: a competition at a party, split by category. PC demo, Amiga demo, 4k intro, tracked music, pixel graphics and so on.</li>
<li value="6">wild: the compo for anything that doesn't fit the others. Videos, hardware hacks, odd machines.</li>
<li value="7">oldschool and newschool: roughly, old machines (C64, Amiga, Atari, Spectrum) versus modern PCs. Each party draws the line in its own way.</li>
<li value="8">capture: a video recording of a prod, made because running the real thing isn't always easy.</li>
<li value="9">handle: the nickname a scener uses. Almost nobody signs with their legal name.</li>
<li value="10">greetings: the shout-outs to other groups, usually somewhere inside the prod itself.</li>
<li value="11">party version and final version: prods are often finished at the last minute, so a fixed version sometimes comes out weeks later.</li>
<li value="12">remote entry: a prod sent to a party by someone who isn't there in person.</li>
</ul><h2 id="step-1-watch-a-capture">Step 1: watch a capture</h2><p>The easiest way in is a video. Almost every well-known prod has a capture on YouTube, and the database sites below link straight to it. Use full screen, turn the sound up and give it the whole running time. Demos are built like music videos, and a lot of them only make sense once the soundtrack kicks in.</p><p>If you want to see the scene live, Revision, the biggest pure demoparty around, streams the whole thing on Twitch at <a href="https://www.twitch.tv/revisionparty">twitch.tv/revisionparty</a>, with a main stage and a separate stream for the seminars. Its YouTube channel is <a href="https://www.youtube.com/@RevisionParty">@RevisionParty</a>. Watching the compos live, with the chat reacting to every effect, is a pretty good way to understand the culture in one evening.</p><p>A capture is a recording, though. It's the photo of the cake, and later in this guide you'll get to eat the cake.</p><h2 id="step-2-find-the-good-stuff">Step 2: find the good stuff</h2><p>There are about 2,000 prods released every year. You need a map.</p><h3 id="pouet">Pouët</h3><p><a href="https://www.pouet.net/">Pouët</a> is where sceners rate and comment on prods. Every prod gets a page with the group, the platform, the party and the place it got in the compo, plus links to the download, the capture and often the source code or a making-of.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/2rzeb30ryqpcwpdxsifkll8q21rw/pouet-second-reality.png" alt="The Pouët page for Second Reality by Future Crew, on a blue background with trumpet patterns. A screenshot of the demo, a gray 3D city seen from above, sits on the left. On the right: platform MS-Dos, type demo, released October 1993 at Assembly 1993, ranked 1st in the PC demo compo, and a long list of remixes. Below are the votes (395 thumbs up, 49 piggies, 16 thumbs down), &quot;alltime top: #14&quot; and links to download, demozoo, github, online version, windows port and youtube." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/MLQUnxXdwq-480.webp 480w, https://pablomurad.com/img/MLQUnxXdwq-800.webp 800w, https://pablomurad.com/img/MLQUnxXdwq-1200.webp 1200w, https://pablomurad.com/img/MLQUnxXdwq-1600.webp 1600w, https://pablomurad.com/img/MLQUnxXdwq-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>The votes come in three flavors: a thumbs up, a thumbs down and a little pig face in the middle, which the site's own FAQ describes as "meh". The front page has an all-time top list and a top of the month list. For a beginner, those two boxes make a very good first playlist.</p><p>A tip about the comments: read them, but don't take them too seriously. Some are deep technical analysis, and some are one-word insults that have been sitting there for decades.</p><h3 id="demozoo">Demozoo</h3><p><a href="https://demozoo.org/">Demozoo</a> is the encyclopedia. Where Pouët is opinion, Demozoo is record: who made what, in which group, at which party, with which credits. You can browse by platform (it lists more than a hundred), by party and by person, and every party page has its full results.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/p24n4qq0nwx3s0opeudbq6zof0tk/demozoo-303-acme.png" alt="The Demozoo page for the demo 303 by Acme, released 5 April 1997 and 1st in the X 1997 PC demo competition, for Linux, MS-Dos and Windows. On the left, a grid of four colorful screenshots with a play button over them. On the right, a list of downloads from scene.org, including fixes, Linux and Windows wrappers and a video capture, then external links to DosDemos.com, Pouët and YouTube, and tags such as dos4gw, midas and vocals-in-music. Credits and a &quot;packed in&quot; box start at the bottom." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/Qh_F5vtylW-480.webp 480w, https://pablomurad.com/img/Qh_F5vtylW-800.webp 800w, https://pablomurad.com/img/Qh_F5vtylW-1200.webp 1200w, https://pablomurad.com/img/Qh_F5vtylW-1600.webp 1600w, https://pablomurad.com/img/Qh_F5vtylW-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Look at the downloads box on a page like this one: party version, fixes, wrappers for modern systems and a video capture, all in one place. For some ZX Spectrum prods, Demozoo can even run the original right in your browser.</p><h3 id="awards-and-yearly-picks">Awards and yearly picks</h3><p>The <a href="https://2027.meteoriks.org/">Meteoriks</a> are the scene's yearly awards, handed out on stage at Revision since 2015. A jury picks the winners in most categories, and there's always a Public Choice and a New Talent award. The list of nominees for a year is a great shortcut to what mattered in that year.</p><p>Another good shortcut is a yearly write-up like <a href="https://laurent.le-brun.eu/blog/the-best-demos-of-2025-from-the-demoscene">The best demos of 2025 from the demoscene</a>, which picks a handful of prods, embeds the videos and links to Pouët.</p><h2 id="step-3-run-the-real-thing">Step 3: run the real thing</h2><p>This is where it gets fun. Running a demo means your own computer is drawing every frame and playing every note right now. For an intro, it also means a file of a few kilobytes is doing all of that.</p><h3 id="modern-windows-prods">Modern Windows prods</h3><p>Most new PC prods are Windows executables. Download them from the link on Pouët or Demozoo, which usually points to scene.org, unzip and run.</p><p>Your antivirus may complain. Size-limited intros squeeze their code with executable packers, and some security tools see packed code as suspicious. The Pouët FAQ says it plainly: files hosted on scene.org can be considered clean. If you're still unsure, read the comments on the prod page. Sceners are quick to shout when something is actually wrong.</p><p>Also read the .nfo file that comes in the zip. It's a small text file with credits, greetings and, often, the hardware the prod needs. A 4k intro that renders everything with shaders can ask a lot from your graphics card, and the .nfo will tell you before your fan does.</p><h3 id="dos">DOS</h3><p>For the classics of the 90s, like Second Reality, you need a DOS emulator. The two most active ones are <a href="https://www.dosbox-staging.org/">DOSBox Staging</a> and <a href="https://dosbox-x.com/">DOSBox-X</a>, both free and available for Windows, macOS and Linux.</p><p>Old demos can be picky. If one crashes, three things usually help: put it in a short folder path (something like C:\REAL), set a fixed number of CPU cycles instead of the automatic setting, and try another sound card option if the music is the problem.</p><p>A nice detail: Future Crew released the source code of Second Reality in 2013, for the demo's twentieth anniversary. It's <a href="https://github.com/mtuomi/SecondReality">on GitHub</a>, and reading it is a lesson in how people wrote software when every byte mattered.</p><h3 id="amiga">Amiga</h3><p>For the Amiga, use <a href="https://www.winuae.net/">WinUAE</a> on Windows, or <a href="https://fs-uae.net/">FS-UAE</a> and <a href="https://amiberry.com/">Amiberry</a> on other systems. The catch is the Kickstart, the Amiga's ROM. The original ROMs are sold legally in <a href="https://www.amigaforever.com/">Amiga Forever</a>, which also bundles a selection of demoscene prods. There's a free replacement called AROS, and WinUAE has it built in, but it runs some software and not all of it, so expect a few demos to fail.</p><h3 id="commodore-64">Commodore 64</h3><p>For the C64, use <a href="https://vice-emu.sourceforge.io/">VICE</a>. The official builds come with the C64 ROMs included, so it works out of the box. On some Linux distributions the packaged version has the ROMs removed, so the official build is the easier path.</p><h3 id="atari-st-and-falcon">Atari ST and Falcon</h3><p><a href="https://www.hatari-emu.org/">Hatari</a> emulates the ST, STE, TT and Falcon. Its official builds ship with EmuTOS, a free replacement for the Atari's operating system, so you can start without hunting for the original TOS.</p><h3 id="zx-spectrum">ZX Spectrum</h3><p><a href="https://fuse-emulator.sourceforge.net/">Fuse</a> runs on Linux, Windows and macOS. And remember that Demozoo can run some Spectrum prods straight in the browser.</p><h2 id="a-watchlist-for-after-the-classics">A watchlist for after the classics</h2><p>The first post already pointed at Second Reality, State of the Art, Desert Dream, fr-08 and Elevated. These are good next steps, grouped by type. Each link goes to the Pouët page, where you'll find the capture and the download.</p><p>Demos for PC:</p><ul>
<li value="1">
<a href="https://www.pouet.net/prod.php?which=30244">fr-041: debris.</a> by Farbrausch, 1st at Breakpoint 2007. A city falling apart, from a download that's famously tiny for what it shows.</li>
<li value="2">
<a href="https://www.pouet.net/prod.php?which=54603">Agenda Circling Forth</a> by Fairlight and CNCD, 1st at Breakpoint 2010.</li>
<li value="3">
<a href="https://www.pouet.net/party.php?which=1550&amp;when=2026">Razor1911</a> by Razor 1911, winner of the PC demo compo at Revision 2026. Razor 1911 started on the C64 in Norway in 1985 and became one of the most famous cracking groups in history. Forty years later, the same name wins the biggest pure demoparty around with a demo. If you read the first post, you know why that's funny.</li>
</ul><p>64k intros:</p><ul>
<li value="1">
<a href="https://www.pouet.net/prod.php?which=5">Heaven Seven</a> by Exceed, 1st at Mekka and Symposium 2000.</li>
<li value="2">
<a href="https://www.pouet.net/prod.php?which=62935">The Timeless</a> by Mercury, 1st at Revision 2014.</li>
<li value="3">
<a href="https://www.pouet.net/prod.php?which=88524">Clean Slate</a> by Conspiracy, 1st at Revision 2021.</li>
<li value="4">Squidopus Famelica by With Love, the 64k winner at Revision 2026. You'll find it in the <a href="https://www.pouet.net/party.php?which=1550&amp;when=2026">Revision 2026 results</a>.</li>
</ul><p>4k intros:</p><ul>
<li value="1">
<a href="https://www.pouet.net/prod.php?which=50063">Atrium</a> by TBC and Loonies, 1st at Breakpoint 2008.</li>
<li value="2">
<a href="https://www.pouet.net/prod.php?which=55758">Cdak</a> by Quite and Orange, 1st at Chaos Constructions 2010.</li>
<li value="3">duo by Nuance, the 4k winner at Revision 2026, also in the results page above.</li>
</ul><p>Amiga:</p><ul>
<li value="1">
<a href="https://www.pouet.net/prod.php?which=25778">Starstruck</a> by The Black Lotus, 1st at Assembly 2006, for the Amiga AGA and the Atari Falcon.</li>
<li value="2">Second Nature by Desire and The Twitch Elite, the Amiga demo winner at Revision 2026.</li>
</ul><p>Commodore 64:</p><ul><li value="1">
<a href="https://www.pouet.net/prod.php?which=51983">Edge of Disgrace</a> by Booze Design, 1st at X 2008.</li></ul><p>A good habit: when a prod hits you, open its Demozoo page, look at the group and watch what else that group made. Do the same with the musician and the graphician in the credits.</p><h2 id="step-4-make-something">Step 4: make something</h2><p>The scene isn't only coders. Every party has compos for music and graphics, and a lot of prods are made by a group where only one person writes code. Pick the door that fits you.</p><h3 id="shaders-right-in-the-browser">Shaders, right in the browser</h3><p>A shader is a small program that runs on the graphics card and decides the color of every pixel on the screen. A big part of modern demos and intros is made of them, and you can start writing one in the next five minutes.</p><p><a href="https://www.shadertoy.com/">Shadertoy</a> is a website where you write shaders in GLSL and see the result instantly. You can browse and edit any shader on the site, change a number and watch what happens. It's a great way to learn: open something beautiful, break it and figure out why it broke.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/er2a06dr6ob5xzr9mu6qlhxbc8c6/shadertoy-home.png" alt="The Shadertoy home page. The shader of the week, &quot;Light Cycle&quot;, fills the left side: a tunnel of glowing cyan and orange lines vanishing toward a dark center. On the right, the headline &quot;Build and Share your best shaders with the world and get Inspired&quot; and a list of the latest contributions. A row of four featured shaders sits below, including organic bone-like shapes, a black and white pattern, colored light beams and blue floating squares." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/i9t6Z6Z28c-480.webp 480w, https://pablomurad.com/img/i9t6Z6Z28c-800.webp 800w, https://pablomurad.com/img/i9t6Z6Z28c-1200.webp 1200w, https://pablomurad.com/img/i9t6Z6Z28c-1600.webp 1600w, https://pablomurad.com/img/i9t6Z6Z28c-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>When you get comfortable, look at live coding. At Revision there's the Shader Showdown: two coders on stage, 25 minutes, an empty editor, a DJ playing, and they write a shader from scratch while the audience watches. No notes, no phones, no USB sticks. The crowd picks the winner by cheering. The tool they use is called <a href="https://github.com/Gargaj/bonzomatic">Bonzomatic</a>, it's free, and you can practice with it at home. There's a whole archive of these events, with the code of each round, at <a href="https://livecode.demozoo.org/">livecode.demozoo.org</a>.</p><h3 id="fantasy-consoles">Fantasy consoles</h3><p>A fantasy console is a computer that never existed, with limits chosen on purpose. It's the closest thing to an 80s home computer you can download today.</p><p><a href="https://tic80.com/">TIC-80</a> is free and open source. It has a 240x136 screen, a 16-color palette, 4 sound channels and built-in editors for code, sprites, maps and sound. There's an optional paid Pro build, but the free one does everything you need to start.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/g1pptjix8llehgfnffgcizjnq6x1/tic80-home.png" alt="The TIC-80 tiny computer website in a pixel font on a dark blue background, with a small smiling computer as its logo and the version v1.4.1. A dark console screen in the middle shows the boot text &quot;TIC-80 tiny computer&quot;. Below it, the site explains that TIC-80 is a free and open source fantasy computer with built-in code, sprite, map and sound editors, a 240x136 pixel display, a 16-color palette, 256 8x8 sprites and 4-channel sound." width="1920" height="1080" loading="lazy" srcset="https://pablomurad.com/img/8kdK7ey_ta-480.webp 480w, https://pablomurad.com/img/8kdK7ey_ta-800.webp 800w, https://pablomurad.com/img/8kdK7ey_ta-1200.webp 1200w, https://pablomurad.com/img/8kdK7ey_ta-1600.webp 1600w, https://pablomurad.com/img/8kdK7ey_ta-1920.webp 1920w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>TIC-80 has become a real scene platform. There are regular byte jams, live sessions where people code effects on it together, and sizecoding parties have whole compos for it. <a href="https://www.lexaloffle.com/pico-8.php">PICO-8</a> is the paid cousin (US$14.99), with a 128x128 screen and 16 colors, and a big community of its own.</p><h3 id="sizecoding">Sizecoding</h3><p>Sizecoding is the art of making something impressive in a ridiculously small file, think 256 bytes, or 128, or 64. This line of the post you're reading is already longer than some of those prods.</p><p>The best place to see it happen is <a href="https://lovebyte.party/">Lovebyte</a>, an online party dedicated to it, usually in February, held on Discord with the compos streamed on Twitch. It has intro compos from 1k down to 32 bytes, split into oldschool and high-end, plus categories for tiny graphics, tiny music and bytebeat, and a compo just for new talent. Since it's online, anyone can take part from home.</p><p>To learn, the <a href="https://www.sizecoding.org/">sizecoding.org</a> wiki is the reference, with a focus on tiny programs for the x86. If you'd rather start with JavaScript, <a href="https://www.dwitter.net/">Dwitter</a> is a site of visual demos written in 140 characters. Every dweet is a little puzzle, and reading other people's is half the fun.</p><h3 id="the-tools-behind-4k-and-64k-intros">The tools behind 4k and 64k intros</h3><p>If you get serious about size-limited intros on Windows, these names will show up again and again:</p><ul>
<li value="1">
<a href="https://github.com/runestubbe/Crinkler">Crinkler</a>: a compressing linker for 1k, 4k and 8k intros. It's what turns your code into a tiny executable.</li>
<li value="2">
<a href="https://www.farbrausch.de/%7Efg/kkrunchy/">kkrunchy</a>: an executable packer made mainly for 64k intros.</li>
<li value="3">
<a href="https://github.com/gopher-atz/4klang">4klang</a>: a synthesizer built for 4k intros. You compose the music, and it becomes a few hundred bytes of code.</li>
<li value="4">
<a href="https://github.com/vsariola/sointu">Sointu</a>: an evolution of 4klang that runs on Windows, Mac, Linux and even in the browser.</li>
<li value="5">
<a href="https://github.com/askeksa/Oidos">Oidos</a>: an additive synthesizer for 4k and 8k intros, with a matching reverb.</li>
</ul><p>They're all free.</p><h3 id="music-trackers">Music: trackers</h3><p>A tracker is the classic way to make scene music. Instead of a piano roll, you get columns of notes scrolling down the screen, like a spreadsheet that plays. It looks strange at first, but the logic is simple: each row is a step in time, and each column is a channel playing its own sound. Every party has a tracked music compo.</p><ul>
<li value="1">
<a href="https://openmpt.org/">OpenMPT</a>: free, for Windows.</li>
<li value="2">
<a href="https://milkytracker.org/">MilkyTracker</a>: free and open source, in the style of FastTracker II, for Windows, macOS and Linux.</li>
<li value="3">
<a href="https://tildearrow.org/furnace/">Furnace</a>: free and open source, for chiptune, emulating the sound chips of many old systems.</li>
<li value="4">
<a href="https://github.com/8bitbubsy/pt2-clone">pt2-clone</a>: a faithful copy of ProTracker, the classic Amiga tracker.</li>
<li value="5">
<a href="https://www.renoise.com/">Renoise</a>: commercial, with a demo version, and the most modern of the bunch.</li>
</ul><h3 id="pixel-graphics">Pixel graphics</h3><p>The graphics compos are mostly pixel art, often with the palette and resolution of a specific old machine.</p><ul>
<li value="1">
<a href="http://grafx2.chez.com/">GrafX2</a>: free and open source, inspired by Deluxe Paint, the program that defined Amiga graphics.</li>
<li value="2">
<a href="https://www.cosmigo.com/">Pro Motion NG</a>: has a free edition and a cheap full version.</li>
<li value="3">
<a href="https://www.aseprite.org/">Aseprite</a>: paid, very popular, and great for animation.</li>
</ul><h2 id="step-5-release-it">Step 5: release it</h2><p>When something is done, release it. The scene is built on releases, and a small finished thing is worth more than a big unfinished one.</p><p>You don't have to travel to enter a compo. Since 2023, Revision accepts remote entries. In 2024 there were 85 of them, next to the entries made on site. Other parties have their own rules, so check the rules page of each one before sending anything. Online parties like Lovebyte are an even easier start.</p><p>When you release, a few habits help:</p><ul>
<li value="1">Include an .nfo file with the title, the group, the credits, the party and the greetings.</li>
<li value="2">Add the prod to Pouët and Demozoo, so it's findable and part of the record.</li>
<li value="3">Make a capture, because most people will watch the video first.</li>
<li value="4">Read the feedback, keep the useful parts and let the rest go.</li>
</ul><h2 id="step-6-go-to-a-party">Step 6: go to a party</h2><p>This is the heart of it. A party is where people finish prods at four in the morning, watch the compos together on a big screen and vote. Most of the big ones are in Europe, and their dates move every year, so always check <a href="https://www.demoparty.net/">demoparty.net</a> or Demozoo before planning. To give you an idea of the calendar:</p><ul>
<li value="1">January: Gerp, in Skövde, Sweden.</li>
<li value="2">February: Lovebyte, online, and BCC Party in Germany (26 to 28 February 2027).</li>
<li value="3">Easter: Revision, in Saarbrücken, Germany.</li>
<li value="4">May: Outline, in Ommen, the Netherlands.</li>
<li value="5">July: Black Valley, near Oslo, Norway (9 to 11 July 2027).</li>
<li value="6">Summer: Assembly, in Helsinki, Finland, and the summer edition of Silly Venture, the Atari party, in Gdańsk, Poland.</li>
<li value="7">August: Evoke, in Cologne, Germany, and Xenium, in Poland.</li>
<li value="8">October: Deadline, in Berlin (1 to 3 October 2027).</li>
<li value="9">November: the winter edition of Silly Venture, also in Gdańsk.</li>
<li value="10">December: Inércia, in Portugal.</li>
</ul><p>Outside Europe, there's Syntax in Australia, Demosplash in the United States and Flashparty in Buenos Aires, the most visible party in Latin America.</p><p>Revision publishes a small guide called <a href="https://2024.revision-party.net/assets/how-to-scene-2024.pdf">How to scene</a> for first-timers, and in 2026 it had a seminar for beginners. If you go, read the guide before you leave home. If you can't go, watch the stream: it's the next best thing.</p><h2 id="where-to-talk-to-people">Where to talk to people</h2><p>The scene still lives in old corners of the internet. The Revision guide lists IRC channels like #newsceners, made for exactly the person reading this, and #revision, plus a Discord server. There's also a general demoscene Discord server, which some parties point newcomers to. Ask questions, show what you made and greet people back when they greet you.</p><p>And if you're in Brazil or anywhere in Latin America and you're part of the scene, or want to be, write to me. Our scene has always been small, and that only changes when people find each other.</p><h2 id="the-short-version">The short version</h2><p>If you read nothing else, do this tonight: open the all-time top on Pouët and watch three captures. Pick the one you liked most and run the real thing. Then open Shadertoy and change one number in a shader you like.</p><p>That's already more than most people ever do with a computer.</p>]]></content>
    </entry>
    <entry>
        <title>Three small computers looking for neighbors</title>
        <link href="https://pablomurad.com/three-small-computers-looking-for-neighbors/"/>
        <id>https://pablomurad.com/three-small-computers-looking-for-neighbors/</id>
        <published>2026-11-06T22:37:58-03:00</published>
        <updated>2026-11-06T22:37:58-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="pubnix"/>
        <summary>I look after three small public Unix machines and all of them have room for you: runv.club on Debian, noctem.cafe on OpenBSD and Noctem 9 on Plan 9. Shell accounts, tilde pages, Gopher, Gemini, IRC and no algorithm. Pick one and come say hi.</summary>
        <content type="html"><![CDATA[<p>I look after three little public computers, and all three have room for you.</p><p>They're pubnixes, short for public Unix systems: machines that a bunch of people share over the internet. You get an account, a home folder, a shell and a web page at an address with a tilde in it, like <code>server/~you</code>. There's no feed, no likes, no algorithm and nothing to scroll. Just files, text, links and whoever else happens to be logged in.</p><p>Each of the three runs a different operating system, which means each one is a different kind of fun. One is a friendly Linux box with a long list of goodies, one is a quiet OpenBSD machine, and one runs Plan 9, the operating system Bell Labs made after Unix. Pick one, or knock on all three.</p><h2 id="runv-club-the-busy-one">runv.club, the busy one</h2><p><a href="https://runv.club">runv.club</a> is a small public Unix server run from Brazil, on Debian. It's the one with the most toys, and it's a good first pubnix if you've never had a shell account anywhere.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/5u6gv6lkua8owqvbpasfblf13sh1/runv-club-home.png" alt="The runv.club home page: plain black serif text on a white background. It opens with &quot;runv.club is a small public Unix server, run from Brazil&quot; and a list under &quot;What you get&quot;: a shell over SSH with many languages, a web page at runv.club/~you with a Gemini capsule and a Gopher hole, a blog that also shows up on the planet, a spot on the runv ring, your own tunnel address, an eepsite on I2P, git, bookmarks and an RSS reader, #runv on tilde.chat, and pb, twtxt, finger, a forum and a dashboard called runv." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/qr-bku0n_B-480.webp 480w, https://pablomurad.com/img/qr-bku0n_B-800.webp 800w, https://pablomurad.com/img/qr-bku0n_B-1200.webp 1200w, https://pablomurad.com/img/qr-bku0n_B-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>When you join, you get:</p><ul>
<li value="1">a shell over SSH or mosh, with editors, git and a pile of languages: Python, Perl, C, Go, Rust, Node, Ruby, Lua, Haskell and a few Lisps</li>
<li value="2">a web page at <code>runv.club/~you</code>, plus a Gemini capsule and a Gopher hole</li>
<li value="3">a blog that writes itself: drop Markdown files in <code>~/blog</code> and they show up on the web, Gemini and Gopher at once, and on the <a href="https://runv.club/planet/">planet</a> that gathers everyone's posts</li>
<li value="4">a spot on the <a href="https://runv.club/ring/">runv ring</a>, our webring</li>
<li value="5">your own address at <code>you.tun.runv.club</code>, for an app running there or a tunnel from your laptop</li>
<li value="6">an eepsite with its own address on I2P</li>
<li value="7">private Git, a bookmark keeper and an RSS reader</li>
<li value="8">a seat in #runv on tilde.chat, through our IRC bouncer, so you stay in the room even with the laptop closed</li>
<li value="9">pastes, twtxt, finger, a forum that lives in the terminal, and a dashboard: just type <code>runv</code>
</li>
</ul><p>The planet is still empty. Your first post would be the first post on it, which is a nice thing to be able to say.</p><p>It's also a great place to learn Linux without breaking your own computer. Every request to join is read by a person, and we let in five people a week, so the place grows slowly. You need to be 16 or older and have an SSH key. Ask at the <a href="https://runv.club/join">join page</a>, or straight from a terminal:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>ssh join@runv.club</span></span></code></pre></div><p>If you want the backstory, I wrote about <a href="https://pablomurad.com/why-we-brought-runv-club-back/">why we brought runv.club back</a> after the first version closed.</p><h2 id="noctem-cafe-the-quiet-one">noctem.cafe, the quiet one</h2><p><a href="https://noctem.cafe">noctem.cafe</a> is a shared Unix machine running OpenBSD, hosted in Minas Gerais. It's small on purpose. Where runv is a busy club, noctem.cafe is a café at night with a couple of tables and a good lamp.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/8l4q6ypo6scl696yunlwh6u4f8wi/noctem-cafe-home.png" alt="The noctem.cafe home page, a classic text page with navy blue headings and thin rules. The title &quot;noctem.cafe, a small shared Unix machine&quot; sits next to a small OpenBSD pufferfish logo. A boxed notice says it's a shared machine hosted in Minas Gerais, Brazil, and small on purpose. Below come the sections About this host, Users (~alice and ~pablo), Services (web, Gopher, Gemini and an I2P address), OpenBSD and Rules of the house." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/rzouSG_5y3-480.webp 480w, https://pablomurad.com/img/rzouSG_5y3-800.webp 800w, https://pablomurad.com/img/rzouSG_5y3-1200.webp 1200w, https://pablomurad.com/img/rzouSG_5y3-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>It's a place for personal pages, shell use, Gopher spaces, Gemini capsules, I2P pages and small experiments. Every page is reachable on the web, on Gopher, on Gemini and through I2P.</p><p>OpenBSD is known for careful engineering, good manual pages and a strong focus on security. If you've always been curious about it, a shell account on someone else's OpenBSD box is the gentlest way in. You can read <code>man</code> pages that actually explain things, poke at <code>ksh</code>, and see how a system feels when everything was designed by the same people.</p><p>The house rules fit in three lines: be kind to the machine, no spam, mining, scanning or abuse, and keep your files simple and your neighbors in mind. Right now there are only two users, so there's plenty of room at the counter. There's no signup form yet. If you'd like a home there, send me a message on Mastodon at @<a href="mailto:pmurad@murad.social">pmurad@murad.social</a> and tell me a little about what you'd do with it.</p><h2 id="noctem-9-the-strange-one">Noctem 9, the strange one</h2><p><a href="https://noctem9.cafe">noctem9.cafe</a> doesn't run Linux or BSD. It runs <a href="http://9front.org/">9front</a>, the living fork of Plan 9 from Bell Labs, the system where everything is a file, the windows are made by <code>rio</code> and the mouse has a lot more to say than on any other desktop.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/3q5dmwcaa7geix0rh69qit6wnso1/noctem9-cafe-home.png" alt="The Noctem 9 home page. The name NOCTEM 9 is spelled out in big dotted blue letters, with a small drawing of Glenda, the Plan 9 bunny, in the corner and the subtitle &quot;a small shared Plan 9 machine, running 9front&quot;. Below are the sections What is this?, Members (~glenda, with a link to grab an account), Getting in, with the drawterm command to connect, and The wall, which anyone can sign with the command mural." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/ovEvI6FmZO-480.webp 480w, https://pablomurad.com/img/ovEvI6FmZO-800.webp 800w, https://pablomurad.com/img/ovEvI6FmZO-1200.webp 1200w, https://pablomurad.com/img/ovEvI6FmZO-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>This is the easiest one to join. Go to the <a href="https://noctem9.cafe/register">sign up page</a>, pick a name and a password, answer how much 3 plus 2 is, and you have an account. Free, one per person, done in a few seconds.</p><p>You don't log in with SSH. You use drawterm, a small program that opens a real Plan 9 screen on your own computer. Download it from <a href="https://drawterm.9front.org/">drawterm.9front.org</a> and run:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>drawterm -h noctem9.cafe -a noctem9.cafe -u yourname</span></span></code></pre></div><p>Type your password at the <code>dp9ik password:</code> prompt, right-click the gray screen, pick New, draw a window, and you're in a Plan 9 shell. It works from a phone too: I wrote a guide to <a href="https://pablomurad.com/9front-in-your-pocket-drawterm-android/">running drawterm on Android</a>.</p><p>Once inside, anything you put in <code>$home/www</code> shows up at <code>noctem9.cafe/~yourname/</code>. Plain HTML, no build step. And there's a wall that everybody can sign: type <code>mural</code> followed by your message, and it shows up at <a href="https://noctem9.cafe/mural.txt">/mural.txt</a> for the whole world to read.</p><p>The machine is honest about its size. One virtual CPU, 1 GB of RAM and about 3.7 GB of disk, shared by everyone, with 60 MB per account. That's plenty for text, a homepage and a lot of curiosity. The only member listed so far is ~glenda, the bunny, and she would like some company.</p><h2 id="why-bother">Why bother</h2><p>Because it's fun, mostly. You log into a computer that isn't yours, type <code>who</code> and see other people's names, read a page that lives one folder away from yours, and leave a note on a wall that no algorithm moderates.</p><p>It's also a good classroom for Unix. You learn by living in it: editing files with a terminal editor, fixing your own page when you break it, reading man pages because you actually need them. Nobody grades you, and the worst thing that can happen is a broken <code>index.html</code>.</p><p>And it's a small way to keep the old web alive. Personal pages at tilde addresses, Gopher holes, Gemini capsules, a shared IRC channel. None of it needs you to have an audience. It only needs you to show up.</p><p>So pick the machine that sounds like you. runv.club if you want company and a lot to play with, noctem.cafe if you want a quiet corner on OpenBSD, Noctem 9 if you want to try an operating system that thinks differently. Or join all three and tell me which one you like best.</p><p>See you on the servers.</p><p>pmurad</p>]]></content>
    </entry>
    <entry>
        <title>Come get lost in weirdnet</title>
        <link href="https://pablomurad.com/come-get-lost-in-weirdnet/"/>
        <id>https://pablomurad.com/come-get-lost-in-weirdnet/</id>
        <published>2026-11-06T10:00:39-03:00</published>
        <updated>2026-11-06T10:00:39-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="smallweb"/>
        <summary>weirdnet.org is a hand-made map of the small, weird, personal web: 45,589 links in 314 categories, a 1996 look, a Random button, a link dump with RSS, old recipes, crazy letters and a guide to starting your own site. Come get lost.</summary>
        <content type="html"><![CDATA[<p>If you miss the web where you'd click a link on someone's homepage, land on a stranger's shrine to a band you've never heard of, and somehow end up two hours later reading about ham radio antennas, I made a place for you.</p><p>It's called <a href="https://weirdnet.org">weirdnet.org</a>, and it's a hand-made map of the small, weird, personal web. Right now it holds 45,589 links, sorted into 314 categories and 899 tags. There's no feed deciding what you see, no ranking, no ads. Just a big pile of human websites and a lot of ways to fall into them.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/uaejs2mjvejteke4jh9b5j94jpue/weirdnet-home.png" alt="The weirdnet.org home page on a white background. At the top, the word weirdnet in a red italic script with a yellow glow, then a row of gray buttons: Directory, Link Dump, What's New, Search, Random, Info. Under them, two lines of small blue links (Crazy letters, Letters of Hart, Weird recipes, Photos, Uncurated, Suggest a site, Start your own site, Notices, Stats, Manifesto), the line &quot;A hand-made map of the small, weird, personal web. 45,000+ links&quot;, a search box and a green digital counter reading 045589. Below, a two-column list of categories in blue Times New Roman with their counts, like Personal sites (6,577), Archives (4,819), Retro web (910), Amateur radio (682) and Zines (507)." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/cKxfhPcwuw-480.webp 480w, https://pablomurad.com/img/cKxfhPcwuw-800.webp 800w, https://pablomurad.com/img/cKxfhPcwuw-1200.webp 1200w, https://pablomurad.com/img/cKxfhPcwuw-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>Yes, it looks like 1996. That's on purpose. Times New Roman, blue and purple links, gray buttons and a green hit counter. The pages are plain HTML with one tiny stylesheet for the whole site, and almost no JavaScript. It loads fast on a bad connection and it'll look the same in ten years.</p><h2 id="where-to-start">Where to start</h2><h3 id="press-random">Press Random</h3><p>It's the easiest way in. The Random button on the home page sends you to one site from the collection, no questions asked. It might be a tiny Neocities page, a forgotten archive or somebody's blog about woodworking. Press it again and you're somewhere else entirely. It's channel surfing for websites.</p><h3 id="the-directory">The Directory</h3><p><a href="https://directory.weirdnet.org">directory.weirdnet.org</a> is the organized side. Sites are filed by category, and the categories go from the obvious to the delightfully specific:</p><ul>
<li value="1">Personal sites (6,577), the heart of the whole thing</li>
<li value="2">Archives (4,819), for when you want to dig</li>
<li value="3">Retro web (910) and Retro computing (302)</li>
<li value="4">512kb club (894), sites that weigh less than half a megabyte</li>
<li value="5">Amateur radio (682), a whole world of call signs and QSL cards</li>
<li value="6">Zines (507), Net art (599) and Subcultures (568)</li>
<li value="7">Paranormal (328), because of course</li>
</ul><p>Every site has a short description, so you know a little about where you're going before you click. And there's a list of all 314 categories plus a tag browser, if you want to go down a very specific hole.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/fx8nnjxhqgearcps8k2cs45fjmuk/weirdnet-directory.png" alt="The Directory page of weirdnet, with the subtitle &quot;...it's a wild, wild web.&quot; Three columns of bold blue category links with counts: Personal sites, Archives, Programming, Visual arts, Web design, Games, Literature, Music scenes, Retro web, 512kb club, Amateur radio, Music, Net art, Subcultures, Zines, Crafts, Anime manga, Wildlife, Religion, Astronomy, Photography, Small web, Software, Paranormal, History, Retro computing, Philosophy, Movies tv, Film animation and Hobbies. Below, links to all 314 categories and to browse by tag, and a &quot;Newest sites&quot; list starting with Susan Shu Chang, marked with a small animated NEW badge." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/7_Lo5Mp63o-480.webp 480w, https://pablomurad.com/img/7_Lo5Mp63o-800.webp 800w, https://pablomurad.com/img/7_Lo5Mp63o-1200.webp 1200w, https://pablomurad.com/img/7_Lo5Mp63o-1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h3 id="the-link-dump">The Link Dump</h3><p><a href="https://links.weirdnet.org">links.weirdnet.org</a> is the messier sibling, with the motto "...i found this and dropped it here." Everything shows up in the order it arrived, newest first, with tags, an archive by month and a search box. On any given page you might find an old GeoCities page next to a 1980 computer animation, a zine scan on the Internet Archive and somebody's essay on Hermetic symbols.</p><p>The Link Dump has RSS and Atom feeds, so you can drop it into your feed reader and get a little stream of the weird web every day.</p><h2 id="the-odd-little-rooms">The odd little rooms</h2><p>Besides the directory, weirdnet has a few corners that have nothing to do with links.</p><p><strong>Crazy letters.</strong> Over a hundred absurd letters, with titles like "Ghost included in the price", "Buying a flea", "Small talk with a frog-man" and "The great stoat revolution".</p><p><strong>Letters of Hart.</strong> A small archive of 36 texts by Michael S. Hart, who started Project Gutenberg in 1971 by typing the Declaration of Independence into a university computer and sharing it on the network. The texts are about books, reading and free access to both.</p><p><strong>Weird recipes.</strong> Forty old recipes, the kind with spelling from another century. Stewed Pompions, Bird's Nest Pudding, Raspberry Shrub, Potatoe Pasty and, yes, Parmesan Ice Cream.</p><p><strong>Photos.</strong> A handful of random trips from before 2008.</p><p><strong>Stats.</strong> A page that looks like an old BBS sysop screen, green on black, reporting the numbers of the whole place, including the line "ALGORITHMS: 0 detected".</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/jge0kevog6w62un3ouyhdpgvy9mj/weirdnet-stats.png" alt="The System Stats page of weirdnet, styled like an old BBS screen: green monospaced text on black inside a border of colons. The header reads &quot;WEIRDNET SYSTEM STATISTICS, human-curated, zero algorithm&quot;, followed by lines like NODE weirdnet.org, SYSOP p. murad, UPTIME 10901 days (since 1997, allegedly), ALGORITHMS 0 detected, LINKS INDEXED 45589, IN DIRECTORY 33459, CATEGORIES 314, TAGS 899, LETTERS 102, RECIPES 40, HART TEXTS 36, and the start of a bar chart of top categories." width="1280" height="900" loading="lazy" srcset="https://pablomurad.com/img/sM4Qb5OhZ--480.webp 480w, https://pablomurad.com/img/sM4Qb5OhZ--800.webp 800w, https://pablomurad.com/img/sM4Qb5OhZ--1200.webp 1200w, https://pablomurad.com/img/sM4Qb5OhZ--1280.webp 1280w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="if-you-want-your-own-corner">If you want your own corner</h2><p>Browsing a few hundred personal sites has a side effect: you start wanting one. The <a href="https://weirdnet.org/indieweb/">Start your own site</a> page is a friendly guide for exactly that moment. It walks through getting a domain, picking a place to publish (Neocities, Nekoweb, Bear Blog, Mataroa, prose.sh and others), turning on a feed and saying who you are with an h-card. No jargon wall, and you can stop whenever you have enough.</p><p>And if you already have a weird, personal, hand-made site, or you know one that belongs in the collection, there's a <a href="https://weirdnet.org/suggest/">Suggest a site</a> page. Personal sites, fan shrines, odd archives, webrings and small tools made with love are all good fits.</p><h2 id="why-it-exists">Why it exists</h2><p>There's a <a href="https://weirdnet.org/manifesto/">manifesto</a> on the site, and one line from it sums up the whole project better than I can here: "The centralized web is a mall with velvet ropes. The decentralized web is a city: messy, alive, uneven, full of tiny lights."</p><p>weirdnet is my attempt at a map of that city. It's free, it has no account to create and nothing to install, and it's open to anyone with a browser and a free afternoon.</p><p>So go on. Open <a href="https://weirdnet.org">weirdnet.org</a>, press Random, and tell me where you landed.</p>]]></content>
    </entry>
    <entry>
        <title>Org mode from zero: a complete, friendly guide</title>
        <link href="https://pablomurad.com/org-mode-from-zero/"/>
        <id>https://pablomurad.com/org-mode-from-zero/</id>
        <published>2026-11-06T08:37:03-03:00</published>
        <updated>2026-11-06T08:37:04-03:00</updated>
        <author><name>Pablo Murad</name></author>
        <category term="emacs"/>
        <summary>A complete, friendly guide to Org mode in Emacs: headings and folding, TODOs, dates and repeaters, the agenda, capture, links, tables with formulas, clocking, code blocks you can run, export to HTML and PDF, a starter config and a cheat sheet.</summary>
        <content type="html"><![CDATA[<p>Org mode is two things that grew up together. It's a way of writing plain text with a little structure (stars for headings, dashes for lists, pipes for tables), and it's the part of Emacs that knows what to do with that text. You write a file that any program on earth can open, and Emacs turns it into an outliner, a to-do list, a calendar, a spreadsheet, a notebook that runs code and a publishing tool.</p><p>It started in 2003 as a better way to fold outlines, and it's been part of Emacs since the mid-2000s. In 2026 it's on version 9.8 and still going. The files you write today will still open decades from now, in any text editor, with no app, no account and no export step, because they're just text.</p><p>This guide starts at the first star and goes all the way to agendas, code blocks and exporting a PDF. You don't need to read it in one sitting. Each section works on its own, and the cheat sheet at the end is there for the day you forget a key.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/k7jlthw0kxj3kvgnalb83lcfxl6e/org-mode-unicorn.png" alt="The Org mode logo: the head of a unicorn in teal green with a rust-colored mane, its eyes closed, and a long spiral horn in gray and white." width="960" height="1043" loading="lazy" srcset="https://pablomurad.com/img/Be-nfzG8cU-480.webp 480w, https://pablomurad.com/img/Be-nfzG8cU-800.webp 800w, https://pablomurad.com/img/Be-nfzG8cU-960.webp 960w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><h2 id="before-you-start">Before you start</h2><p>You need Emacs. If you don't have it yet, my post <a href="https://pablomurad.com/emacs-for-the-curious/">Emacs for the curious</a> covers installing it on Linux and Windows and surviving the first day. Org already comes inside Emacs, so there's nothing else to install. If you ever want the newest Org, it's on GNU ELPA, the official package archive.</p><p>A quick reminder of how Emacs keys are written, because this post is full of them:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c: hold Ctrl and press c</span></span>
<span class="line"><span>M-x: hold Alt and press x (or press Esc, then x)</span></span>
<span class="line"><span>C-c C-t: Ctrl+c, then Ctrl+t</span></span>
<span class="line"><span>S-TAB: Shift+Tab</span></span>
<span class="line"><span>M-RET: Alt+Enter</span></span></code></pre></div><p>To start, open a file whose name ends in <code>.org</code>, for example <code>C-x C-f ~/org/inbox.org</code>. Emacs sees the extension and switches to Org mode on its own. Everything below happens in that file.</p><h2 id="one-star-two-stars">One star, two stars</h2><p>A heading is a line that starts with one or more stars and a space. More stars, deeper level:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* Home</span></span>
<span class="line"><span>** Kitchen</span></span>
<span class="line"><span>*** Fix the dripping tap</span></span>
<span class="line"><span>** Garden</span></span>
<span class="line"><span>* Work</span></span>
<span class="line"><span>** Blog</span></span>
<span class="line"><span>*** Write the Org post</span></span></code></pre></div><p>Everything under a heading, until the next heading of the same level, belongs to it. That's the whole idea of an outline, and it's what makes the next trick possible.</p><h3 id="folding-with-tab">Folding with Tab</h3><p>Put the cursor on a heading and press <code>TAB</code>. The heading closes and shows three dots, meaning there's something inside. Press <code>TAB</code> again and you see the children. Once more and everything under it opens. Org calls this cycling: folded, children, everything.</p><p><code>S-TAB</code> does the same for the whole file at once. Press it a few times and the file goes from only top-level headings, to all headings, to every line of text.</p><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/9wsdbemqf3idto04h1t4czya5ith/org-mode-overview.png" alt="An Org file called myfirstorgfile.org open in Emacs, in overview mode. Only two headings are visible, &quot;Prepare the trip...&quot; on line 1 and &quot;The trip...&quot; on line 15, each ending in three dots to show folded content. The bottom of the window reads OVERVIEW." width="640" height="400" loading="lazy" srcset="https://pablomurad.com/img/nl-tEJlDwG-480.webp 480w, https://pablomurad.com/img/nl-tEJlDwG-640.webp 640w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><figure class="attachment attachment--image"><img src="https://pablomurad.com/media/fm5y2wakb3fv35qoiykfynbqrb9c/org-mode-show-all.png" alt="The same Org file after pressing Shift+Tab until SHOW ALL appears at the bottom. Under &quot;Prepare the trip&quot; there's a subheading &quot;Plan it&quot; with a small table of three days (flight, go through the city, flight back), then &quot;Buy what you need&quot; with a numbered list: tickets to and from Berlin, insurance, book hotel, Panama hat. Under &quot;The trip&quot; there's &quot;Taxi to airport&quot; and a line with a link to Wikipedia Berlin and the words important and Berlin in bold and underline." width="640" height="400" loading="lazy" srcset="https://pablomurad.com/img/l5-PrtJ_zf-480.webp 480w, https://pablomurad.com/img/l5-PrtJ_zf-640.webp 640w" sizes="(min-width: 44rem) 42rem, 100vw" decoding="async"></figure><p>This is the first thing that makes Org feel different from a normal text file. A file with three hundred lines can look like five lines, and you open only the part you're working on.</p><h3 id="moving-things-around">Moving things around</h3><p>You never need to cut and paste a section in Org. You grab it with the keyboard and move it:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>M-RET: new heading at the same level</span></span>
<span class="line"><span>C-RET: new heading after the whole current section</span></span>
<span class="line"><span>M-left / M-right: promote or demote this heading</span></span>
<span class="line"><span>M-S-left / M-S-right: promote or demote it with all its children</span></span>
<span class="line"><span>M-up / M-down: move the whole section up or down, children included</span></span></code></pre></div><p>Try <code>M-down</code> on "Kitchen" in the example above. The heading jumps below "Garden" and takes the dripping tap with it.</p><h2 id="writing-inside-headings">Writing inside headings</h2><p>Under a heading you write normal paragraphs. Org gives you a handful of marks for inline styles:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>*bold* /italic/ _underline_ =verbatim= ~code~ +strike-through+</span></span></code></pre></div><p>Lists are as simple as they look. Dashes or plus signs for bullets, numbers for ordered lists, and a double colon for term and definition:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>- eggs</span></span>
<span class="line"><span>- flour</span></span>
<span class="line"><span>1. preheat the oven</span></span>
<span class="line"><span>2. mix everything</span></span>
<span class="line"><span>- Org :: an outliner made of plain text</span></span></code></pre></div><p><code>M-RET</code> inside a list adds a new item, and <code>M-up</code> and <code>M-down</code> move items around, the same keys you use for headings. Org also renumbers ordered lists for you when you move things.</p><h3 id="checkboxes">Checkboxes</h3><p>A checkbox is a pair of square brackets with a space inside, placed right after the dash. The fastest way to make one is to let Org write it: put the cursor on a list item and press <code>C-u C-c C-c</code>. Then <code>C-c C-c</code> ticks it, and the empty box becomes <code>[X]</code>. In a list that already has checkboxes, <code>M-S-RET</code> adds a new item with an empty box.</p><p>Add a cookie to the heading above the list, <code>[/]</code> or <code>[%]</code>, and Org keeps count for you:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* Packing for the weekend [2/2]</span></span>
<span class="line"><span>- [X] passport</span></span>
<span class="line"><span>- [X] charger</span></span></code></pre></div><p>Every time you tick or untick a box, the cookie updates. Untick the charger and the heading goes back to <code>[1/2]</code>. <code>[%]</code> shows a percentage instead.</p><h2 id="todo-turning-notes-into-tasks">TODO: turning notes into tasks</h2><p>Any heading becomes a task when it starts with a keyword:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO Renew the domain</span></span>
<span class="line"><span>* DONE Pay the electricity bill</span></span></code></pre></div><p><code>C-c C-t</code> cycles the heading through no keyword, TODO and DONE. <code>S-left</code> and <code>S-right</code> do the same thing in both directions.</p><h3 id="your-own-keywords">Your own keywords</h3><p>Two states are enough for a shopping list, but real work has more steps. Put a line like this at the top of a file:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>#+TODO: TODO NEXT WAIT | DONE CANCELED</span></span></code></pre></div><p>Everything before the bar is open, everything after it is finished. Press <code>C-c C-c</code> on that line so Org reloads it, and now <code>C-c C-t</code> walks through all five. The config at the end of this post sets the same keywords for every file.</p><h3 id="remembering-when-you-finished">Remembering when you finished</h3><p>Add this to your config and Org stamps the moment you close a task:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-log-done </span><span style="color:#666666">'</span><span style="color:#C99076">time</span><span style="color:#666666">)</span></span></code></pre></div><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* DONE Pay the electricity bill</span></span>
<span class="line"><span>CLOSED: [2026-11-06 Fri 10:42]</span></span></code></pre></div><p>It looks like a small thing until the day someone asks "when did we send that?" and the answer is sitting right under the heading.</p><h3 id="priorities">Priorities</h3><p>A priority cookie goes right after the keyword:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO [#A] Back up the server before the update</span></span>
<span class="line"><span>* TODO [#C] Reorganize the bookmarks</span></span></code></pre></div><p><code>A</code> is the highest and <code>C</code> the lowest. <code>S-up</code> and <code>S-down</code> on a heading change it, and <code>C-c ,</code> asks for it directly.</p><h3 id="tags">Tags</h3><p>Tags go at the end of the heading, between colons:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO Call the accountant :work:phone:</span></span>
<span class="line"><span>* Trip to Ouro Preto :travel:</span></span>
<span class="line"><span>** TODO Book the hotel</span></span></code></pre></div><p><code>C-c C-q</code> adds or edits tags with completion. Children inherit their parents' tags, so "Book the hotel" counts as <code>:travel:</code> without saying it. That matters later, when you search by tag.</p><h2 id="dates-scheduled-deadline-and-plain">Dates: scheduled, deadline and plain</h2><p>Org understands dates written between angle brackets:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>&lt;2026-11-10 Tue&gt;</span></span>
<span class="line"><span>&lt;2026-11-10 Tue 14:00&gt;</span></span></code></pre></div><p>You don't type them by hand. <code>C-c .</code> opens a little calendar and a prompt, and the prompt is smarter than it looks. You can type a date, but also things like these:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>fri: next Friday</span></span>
<span class="line"><span>+3d: three days from today</span></span>
<span class="line"><span>+2w: two weeks from today</span></span>
<span class="line"><span>nov 20: the 20th of November</span></span>
<span class="line"><span>2pm: today at two in the afternoon</span></span></code></pre></div><p><code>S-left</code> and <code>S-right</code> on a date move it a day back or forward, and <code>S-up</code> and <code>S-down</code> change whatever part the cursor is on, year, month, day or hour.</p><h3 id="scheduled-and-deadline">SCHEDULED and DEADLINE</h3><p>Two kinds of dates mean something special to a task, and the difference is worth learning early:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO Write the newsletter</span></span>
<span class="line"><span>SCHEDULED: &lt;2026-11-09 Mon&gt;</span></span>
<span class="line"><span>* TODO Send the tax documents</span></span>
<span class="line"><span>DEADLINE: &lt;2026-11-20 Fri&gt;</span></span></code></pre></div><p>SCHEDULED means "this is when I plan to start working on it". DEADLINE means "this must be done by then". <code>C-c C-s</code> adds the first and <code>C-c C-d</code> the second. A deadline starts showing up in the agenda 14 days before the date, so it never catches you by surprise. You can change that window per task: <code>&lt;2026-11-20 Fri -5d&gt;</code> warns you only five days before.</p><h3 id="things-that-repeat">Things that repeat</h3><p>Add a repeater to a date and the task comes back after you mark it done:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO Water the plants</span></span>
<span class="line"><span>SCHEDULED: &lt;2026-11-09 Mon +1w&gt;</span></span>
<span class="line"><span>* TODO Check the backups</span></span>
<span class="line"><span>SCHEDULED: &lt;2026-11-09 Mon ++1w&gt;</span></span>
<span class="line"><span>* TODO Change the water filter</span></span>
<span class="line"><span>SCHEDULED: &lt;2026-11-09 Mon .+3m&gt;</span></span></code></pre></div><p>The three flavors behave differently when you're late:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>+1w: moves exactly one week from the old date, even if that's still in the past</span></span>
<span class="line"><span>++1w: moves forward in steps of one week until it lands in the future</span></span>
<span class="line"><span>.+3m: moves three months from the day you actually did it</span></span></code></pre></div><p><code>+</code> is good for bills, <code>++</code> for weekly rituals that should stay on the same weekday, and <code>.+</code> for chores where the interval matters more than the calendar, like a filter that lasts three months from the day you changed it.</p><h3 id="dates-that-don-t-nag">Dates that don't nag</h3><p>Square brackets make an inactive date: <code>[2026-11-06 Fri]</code>. It's a note of when something happened, and the agenda ignores it. <code>C-c !</code> inserts one. Org itself uses them for the CLOSED stamp and for logs.</p><h2 id="the-agenda-every-file-one-view">The agenda: every file, one view</h2><p>Up to here everything lived in one file. The agenda is where Org starts to feel like an app. It reads all your Org files and builds views out of them: what's scheduled this week, every open task, everything tagged <code>:work:</code>.</p><p>First, the agenda needs a key that works everywhere in Emacs. The Org manual suggests these three, and the rest of this post assumes you have them:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">global-set-key</span><span style="color:#666666"> (</span><span style="color:#B8A965">kbd</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">C-c l</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#666666"> #'</span><span style="color:#C99076">org-store-link</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">global-set-key</span><span style="color:#666666"> (</span><span style="color:#B8A965">kbd</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">C-c a</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#666666"> #'</span><span style="color:#C99076">org-agenda</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">global-set-key</span><span style="color:#666666"> (</span><span style="color:#B8A965">kbd</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">C-c c</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#666666"> #'</span><span style="color:#C99076">org-capture</span><span style="color:#666666">)</span></span></code></pre></div><p>Second, the agenda needs to know which files to read. The simplest way is to keep all your Org files in one folder and point it there:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-agenda-files </span><span style="color:#666666">'(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">~/org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">))</span></span></code></pre></div><p>You can also add the file you're in with <code>C-c [</code>. Now press <code>C-c a</code> and a menu appears. These are the views you'll use most:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>a: the agenda for this week, with scheduled items and deadlines</span></span>
<span class="line"><span>t: every open task, from every file</span></span>
<span class="line"><span>m: match tags, like work or travel+urgent</span></span>
<span class="line"><span>s: search for words across all your files</span></span></code></pre></div><h3 id="living-in-the-agenda">Living in the agenda</h3><p>The agenda is a live view, so you can work from inside it without opening the files:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>f / b: next or previous week</span></span>
<span class="line"><span>.: jump back to today</span></span>
<span class="line"><span>d / w: day view or week view</span></span>
<span class="line"><span>t: change the TODO state of the item under the cursor</span></span>
<span class="line"><span>C-c C-s / C-c C-d: schedule or set a deadline</span></span>
<span class="line"><span>RET: open the item in its file</span></span>
<span class="line"><span>TAB: open it in the other window</span></span>
<span class="line"><span>I / O: clock in or out (more on that below)</span></span>
<span class="line"><span>r: refresh the view</span></span>
<span class="line"><span>s: save all Org files</span></span>
<span class="line"><span>q: close the agenda</span></span></code></pre></div><p>The first time you press <code>C-c a a</code> on a Monday and see your week laid out, with deadlines from three different files already sorted by day, Org stops being a text format and starts being a planner.</p><h3 id="your-own-views">Your own views</h3><p>When a view becomes a habit, give it a key. This one shows today's agenda with every task marked NEXT under it:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-agenda-custom-commands</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">d</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Today and next</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">((</span><span style="color:#DBD7CAEE">agenda </span><span style="color:#C98A7D77">""</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">org-agenda-span </span><span style="color:#666666">'</span><span style="color:#C99076">day</span><span style="color:#666666">)))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">todo </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">NEXT</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))))</span></span></code></pre></div><p>Now <code>C-c a d</code> is your morning page.</p><h2 id="capture-write-it-down-before-you-forget">Capture: write it down before you forget</h2><p>Ideas show up while you're doing something else. Capture lets you write them down without leaving what you're doing. Press <code>C-c c</code> from anywhere in Emacs, pick a template, type a line, press <code>C-c C-c</code>, and you're back where you were. The note went to the right file in the right shape.</p><p>Templates are short:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-capture-templates</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">t</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Task</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> entry </span><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">inbox.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">* TODO %?</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D">%U</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">n</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Note</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> entry </span><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">inbox.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">* %? :note:</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D">%U</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">j</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Journal</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> entry </span><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file+olp+datetree </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">journal.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">* %&lt;%H:%M&gt; %?</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span></code></pre></div><p>The little codes are placeholders. <code>%?</code> is where the cursor waits for you, <code>%U</code> is the current date and time as an inactive date, and <code>%&lt;%H:%M&gt;</code> is the time in whatever format you like. <code>file+olp+datetree</code> files each entry under year, month and day headings, which turns <code>journal.org</code> into a diary that organizes itself. A relative file name like <code>inbox.org</code> lives inside <code>org-directory</code>, the folder you set in the config.</p><p>Inside the capture window you have three choices:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c C-c: save it and go back</span></span>
<span class="line"><span>C-c C-w: save it somewhere else (refile, see below)</span></span>
<span class="line"><span>C-c C-k: never mind, throw it away</span></span></code></pre></div><h2 id="refile-and-archive-keeping-it-tidy">Refile and archive: keeping it tidy</h2><p>An inbox only works if it gets emptied. <code>C-c C-w</code> on any heading (refile) asks where it should live and moves it there, children and all. With this in your config, the list of destinations includes every heading up to three levels deep in your agenda files:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-refile-targets </span><span style="color:#666666">'((</span><span style="color:#B8A965">org-agenda-files</span><span style="color:#666666"> :</span><span style="color:#C99076">maxlevel</span><span style="color:#CB7676"> .</span><span style="color:#4C9A91"> 3</span><span style="color:#666666">)))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-refile-use-outline-path </span><span style="color:#666666">'</span><span style="color:#C99076">file</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-outline-path-complete-in-steps </span><span style="color:#4D9375">nil</span><span style="color:#666666">)</span></span></code></pre></div><p>Finished projects can leave the stage too. <code>C-c C-x C-a</code> archives a heading: by default it moves it to an archive file next to the original, like <code>inbox.org_archive</code>. Nothing gets deleted, the agenda just stops seeing it.</p><p>One simple rhythm: capture everything into <code>inbox.org</code> during the week, then once a week open it, refile each item to its project, and archive what's done.</p><h2 id="links">Links</h2><p>Org links use double brackets, with an optional description:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>[[https://orgmode.org][the Org website]]</span></span>
<span class="line"><span>[[file:~/org/projects.org][my projects]]</span></span>
<span class="line"><span>[[file:~/Pictures/rack.jpg]]</span></span>
<span class="line"><span>[[*Write the Org post]]</span></span></code></pre></div><p>The last one points to a heading in the same file. When the description is there, Org shows only the description, underlined and clickable, and hides the brackets.</p><p>The keys:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c l: store a link to wherever you are (any file, any heading, even an email in Emacs)</span></span>
<span class="line"><span>C-c C-l: insert a link, or edit the one under the cursor</span></span>
<span class="line"><span>C-c C-o: open the link under the cursor</span></span>
<span class="line"><span>C-c &amp;: go back to where you were before you followed it</span></span>
<span class="line"><span>C-c C-x C-v: show linked images inside the buffer</span></span></code></pre></div><p><code>C-c l</code> and <code>C-c C-l</code> together are the trick. Store a link to a heading deep inside some project file, go to your journal, press <code>C-c C-l</code> and the stored link is right there in the prompt. That's how a pile of files turns into a web of notes.</p><h2 id="tables-that-align-themselves">Tables that align themselves</h2><p>Start a line with a pipe, type the column names separated by pipes, and press <code>TAB</code>:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>|Item|Qty|Price|Total|</span></span>
<span class="line"><span>|-</span></span></code></pre></div><p>Org aligns the columns. The second line, a pipe and a dash, turns into a separator line when you press <code>TAB</code> on it. From then on, <code>TAB</code> jumps to the next cell and <code>RET</code> to the next row, and the table realigns itself as you type. You can write as messy as you want.</p><p>Moving and changing the table uses the same keys as headings, which is a nice touch:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>M-left / M-right: move a column</span></span>
<span class="line"><span>M-up / M-down: move a row</span></span>
<span class="line"><span>M-S-right: insert a column</span></span>
<span class="line"><span>M-S-left: delete a column</span></span>
<span class="line"><span>M-S-down: insert a row</span></span>
<span class="line"><span>M-S-up: delete a row</span></span>
<span class="line"><span>C-c -: insert a separator line below</span></span>
<span class="line"><span>C-c ^: sort by the current column</span></span></code></pre></div><h3 id="formulas">Formulas</h3><p>Tables can calculate. Here's a small shopping list:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>| Item | Qty | Price | Total |</span></span>
<span class="line"><span>|-</span></span>
<span class="line"><span>| Coffee | 2 | 18 | |</span></span>
<span class="line"><span>| Bread | 3 | 7.5 | |</span></span>
<span class="line"><span>| Cheese | 1 | 32 | |</span></span>
<span class="line"><span>|-</span></span>
<span class="line"><span>| Total | | | |</span></span>
<span class="line"><span>#+TBLFM: $4=$2*$3::@&gt;$4=vsum(@2..@-1)</span></span></code></pre></div><p>Put the cursor on the <code>#+TBLFM</code> line and press <code>C-c C-c</code>. The Total column fills in with 36, 22.5 and 32, and the last row adds them up to 90.5.</p><p>The formula reads like this. <code>$4=$2*$3</code> says "column 4 is column 2 times column 3", for every row. After the double colon comes a second formula: <code>@&gt;$4</code> is the last row, column 4, and <code>vsum(@2..@-1)</code> sums from row 2 to the row above. Change a price, press <code>C-c C-c</code> again, and everything updates.</p><p>You don't have to learn the syntax by heart. In any cell, <code>C-c =</code> asks for a formula for that column, and <code>C-c +</code> adds up the numbers in the column above and shows the result. For anything bigger, Org's tables can use Emacs Calc or Lisp in formulas, but the basics carry you a long way.</p><h2 id="properties-and-column-view">Properties and column view</h2><p>Any heading can carry a drawer of key and value pairs:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO Replace the NAS disks</span></span>
<span class="line"><span>:PROPERTIES:</span></span>
<span class="line"><span>:Effort: 2:00</span></span>
<span class="line"><span>:Cost: 1200</span></span>
<span class="line"><span>:END:</span></span></code></pre></div><p><code>C-c C-x p</code> adds a property with completion, and <code>C-c C-x e</code> sets the effort estimate. On their own, properties are just tidy metadata. Combined with column view, they become a spreadsheet of your outline. Add this line at the top of the file:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>#+COLUMNS: %40ITEM %TODO %Effort %Cost</span></span></code></pre></div><p>Then press <code>C-c C-x C-c</code> on a heading. The outline turns into a table, one row per heading, one column per property, and you can edit values right there. <code>q</code> goes back to normal.</p><h2 id="clocking-time">Clocking time</h2><p>Org can tell you where your hours went. On a task, press <code>C-c C-x C-i</code> to start the clock. The mode line at the bottom of Emacs shows the task and the running time. <code>C-c C-x C-o</code> stops it. Org writes the session under the heading:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>* TODO Write the Org post</span></span>
<span class="line"><span>:LOGBOOK:</span></span>
<span class="line"><span>CLOCK: [2026-11-06 Fri 09:10]--[2026-11-06 Fri 11:25] =&gt; 2:15</span></span>
<span class="line"><span>:END:</span></span></code></pre></div><p>A few related keys:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c C-x C-j: jump to the task that's being clocked</span></span>
<span class="line"><span>C-c C-x C-q: cancel the current clock</span></span>
<span class="line"><span>C-c C-x C-d: show the total time on every heading</span></span>
<span class="line"><span>M-x org-clock-report: insert a table with totals per task</span></span></code></pre></div><p>The clock report is a block that updates with <code>C-c C-c</code>, so a monthly "where did my time go" is one key away. In the agenda, <code>I</code> and <code>O</code> clock the item under the cursor in and out.</p><h2 id="code-blocks-a-notebook-in-plain-text">Code blocks: a notebook in plain text</h2><p>This is where Org goes past what a note app does. An Org file can hold code, run it and keep the output:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>#+begin_src shell :results output</span></span>
<span class="line"><span>df -h /</span></span>
<span class="line"><span>#+end_src</span></span></code></pre></div><p>Put the cursor inside and press <code>C-c C-c</code>. Org runs the command and writes the result right under the block, in a <code>#+RESULTS:</code> section. Run it again and the result is replaced.</p><p>Out of the box, only Emacs Lisp runs. To enable other languages, list them in your config:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">org-babel-do-load-languages</span></span>
<span class="line"><span style="color:#666666">'</span><span style="color:#C99076">org-babel-load-languages</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#DBD7CAEE">emacs-lisp </span><span style="color:#CB7676">.</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">python </span><span style="color:#CB7676">.</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">shell</span><span style="color:#CB7676"> .</span><span style="color:#C99076"> t</span><span style="color:#666666">)))</span></span></code></pre></div><p>Now Python works the same way:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>#+begin_src python :results output</span></span>
<span class="line"><span>print(2 + 2)</span></span>
<span class="line"><span>#+end_src</span></span>
<span class="line"><span></span></span>
<span class="line"><span>#+RESULTS:</span></span>
<span class="line"><span>: 4</span></span></code></pre></div><p>A few things that make code blocks pleasant:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c C-,: insert a block from a menu (src, quote, example and more)</span></span>
<span class="line"><span>C-c ': edit the block in a full buffer for that language, then C-c ' again to come back</span></span>
<span class="line"><span>C-c C-c: run the block under the cursor</span></span>
<span class="line"><span>C-c C-v t: write all blocks marked with :tangle into their own files</span></span></code></pre></div><p>That last key is the start of what people call literate configuration. You write your Emacs config, your shell scripts or your server notes as an Org document, with explanations between the code, and add <code>:tangle</code> to each block:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>#+begin_src shell :tangle ~/bin/backup.sh</span></span>
<span class="line"><span>rsync -a ~/org/ server:/backups/org/</span></span>
<span class="line"><span>#+end_src</span></span></code></pre></div><p><code>C-c C-v t</code> writes the code out to the real files. The documentation and the code can't drift apart, because they're the same file.</p><p>If you prefer typing to menus, add <code>(require 'org-tempo)</code> to your config. Then <code>&lt;s</code> followed by <code>TAB</code> expands into a source block, <code>&lt;q</code> into a quote, <code>&lt;e</code> into an example.</p><p>When Org asks "Evaluate this code block?" before running anything, that's on purpose. A file you download could contain code, and Org won't run it without asking.</p><h2 id="export-from-text-to-html-pdf-and-friends">Export: from text to HTML, PDF and friends</h2><p><code>C-c C-e</code> opens the export menu. Each letter picks a format, and a second letter picks what to do with it:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>h h: HTML file</span></span>
<span class="line"><span>h o: HTML file, then open it in the browser</span></span>
<span class="line"><span>l p: PDF, through LaTeX</span></span>
<span class="line"><span>l o: PDF, then open it</span></span>
<span class="line"><span>o o: OpenDocument text (.odt), which LibreOffice and Word open</span></span>
<span class="line"><span>t U: plain text with Unicode, in a buffer</span></span></code></pre></div><p>PDF export needs a LaTeX installation on your computer, such as TeX Live. HTML and ODT need nothing extra.</p><p>Markdown is not loaded by default. Add <code>(require 'ox-md)</code> to your config and <code>m m</code> appears in the menu.</p><p>A few lines at the top of a file control the export:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>#+TITLE: A trip to Ouro Preto</span></span>
<span class="line"><span>#+AUTHOR: Your name</span></span>
<span class="line"><span>#+DATE: 2026-11-06</span></span>
<span class="line"><span>#+OPTIONS: toc:nil num:nil ^:{}</span></span></code></pre></div><p><code>toc:nil</code> removes the table of contents, <code>num:nil</code> removes the numbers in front of headings, and <code>^:{}</code> stops Org from turning <code>file_name</code> into "file" with a subscript "name", which gets in the way when you write about code. In the export menu, <code>C-s</code> limits the export to the heading under the cursor, handy for sending one section of a big file.</p><p>Code blocks export only the code by default. Add <code>:exports both</code> to a block to include its result, or <code>:exports results</code> to show only the output.</p><h2 id="small-things-worth-knowing">Small things worth knowing</h2><p><strong>Footnotes.</strong> <code>C-c C-x f</code> creates a footnote like <code>[fn:1]</code> and takes you to where its text goes. Press it again on the reference to jump back.</p><p><strong>Focus on one section.</strong> <code>C-x n s</code> narrows the buffer to the current heading, so the rest of the file disappears. <code>C-x n w</code> brings it back. Great for writing long things.</p><p><strong>Jump to any heading.</strong> <code>C-c C-j</code> lets you search the outline and go straight to a heading.</p><p><strong>Sparse trees.</strong> <code>C-c /</code> opens a menu that folds the file to show only what matches. <code>C-c / t</code> shows only open tasks, <code>C-c / m</code> matches tags, <code>C-c / r</code> matches a regular expression. Everything else folds away until you press <code>S-TAB</code>.</p><p><strong>Notes on a task.</strong> <code>C-c C-z</code> adds a dated note under the heading. Useful for "called them, they'll answer on Monday".</p><p><strong>A cleaner look.</strong> <code>(setq org-startup-indented t)</code> indents text to match its heading, without changing the file. The stars of deeper levels become invisible except the last one.</p><p><strong>Special characters.</strong> Type <code>\alpha</code> or <code>\rarr</code> and Org shows α and → if you set <code>(setq org-pretty-entities t)</code>. The file still holds the plain backslash version.</p><h2 id="a-starter-config">A starter config</h2><p>Here's everything from this post in one place. Put it in your <code>init.el</code>, change the folder if you want, restart Emacs, and create <code>~/org</code> if it doesn't exist:</p><div class="code-block" data-lang="elisp"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="elisp"><code><span class="line"><span style="color:#758575DD">;;; Org mode, a starting point</span></span>
<span class="line"></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">require</span><span style="color:#666666"> '</span><span style="color:#C99076">org</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">require</span><span style="color:#666666"> '</span><span style="color:#C99076">org-tempo</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; &lt;s TAB for code blocks</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">require</span><span style="color:#666666"> '</span><span style="color:#C99076">ox-md</span><span style="color:#666666">)</span><span style="color:#758575DD"> ; Markdown export</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Keys that work everywhere in Emacs</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">global-set-key</span><span style="color:#666666"> (</span><span style="color:#B8A965">kbd</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">C-c l</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#666666"> #'</span><span style="color:#C99076">org-store-link</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">global-set-key</span><span style="color:#666666"> (</span><span style="color:#B8A965">kbd</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">C-c a</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#666666"> #'</span><span style="color:#C99076">org-agenda</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">global-set-key</span><span style="color:#666666"> (</span><span style="color:#B8A965">kbd</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">C-c c</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span><span style="color:#666666"> #'</span><span style="color:#C99076">org-capture</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Where your files live</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-directory </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">~/org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-default-notes-file </span><span style="color:#666666">(</span><span style="color:#B8A965">expand-file-name</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">inbox.org</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> org-directory</span><span style="color:#666666">))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-agenda-files </span><span style="color:#666666">(</span><span style="color:#4D9375">list</span><span style="color:#DBD7CAEE"> org-directory</span><span style="color:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Task states: open ones before the bar, finished ones after</span></span>
<span class="line"><span style="color:#758575DD">;; The letters are shortcuts; @ asks for a note when you pick that state</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-todo-keywords</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#DBD7CAEE">sequence </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">TODO(t)</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">NEXT(n)</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">WAIT(w@)</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">|</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">DONE(d)</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">CANCELED(c@)</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Record when tasks are done, and keep logs folded in a drawer</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-log-done </span><span style="color:#666666">'</span><span style="color:#C99076">time</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-log-into-drawer </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Capture</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-capture-templates</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">t</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Task</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> entry </span><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">inbox.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">* TODO %?</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D">%U</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">n</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Note</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> entry </span><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">inbox.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">* %? :note:</span><span style="color:#666666">\</span><span style="color:#C99076">n</span><span style="color:#C98A7D">%U</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">j</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Journal</span><span style="color:#C98A7D77">"</span><span style="color:#DBD7CAEE"> entry </span><span style="color:#666666">(</span><span style="color:#DBD7CAEE">file+olp+datetree </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">journal.org</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">* %&lt;%H:%M&gt; %?</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Refile anywhere up to three levels deep</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-refile-targets </span><span style="color:#666666">'((</span><span style="color:#B8A965">org-agenda-files</span><span style="color:#666666"> :</span><span style="color:#C99076">maxlevel</span><span style="color:#CB7676"> .</span><span style="color:#4C9A91"> 3</span><span style="color:#666666">)))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-refile-use-outline-path </span><span style="color:#666666">'</span><span style="color:#C99076">file</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-outline-path-complete-in-steps </span><span style="color:#4D9375">nil</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; A custom agenda view: C-c a d</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-agenda-custom-commands</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">d</span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D77"> "</span><span style="color:#C98A7D">Today and next</span><span style="color:#C98A7D77">"</span></span>
<span class="line"><span style="color:#666666">((</span><span style="color:#DBD7CAEE">agenda </span><span style="color:#C98A7D77">""</span><span style="color:#666666"> ((</span><span style="color:#DBD7CAEE">org-agenda-span </span><span style="color:#666666">'</span><span style="color:#C99076">day</span><span style="color:#666666">)))</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">todo </span><span style="color:#C98A7D77">"</span><span style="color:#C98A7D">NEXT</span><span style="color:#C98A7D77">"</span><span style="color:#666666">)))))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Looks</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-startup-indented </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#CB7676">setq</span><span style="color:#DBD7CAEE"> org-pretty-entities </span><span style="color:#C99076">t</span><span style="color:#666666">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#758575DD">;; Languages for code blocks</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">org-babel-do-load-languages</span></span>
<span class="line"><span style="color:#666666">'</span><span style="color:#C99076">org-babel-load-languages</span></span>
<span class="line"><span style="color:#666666">'((</span><span style="color:#DBD7CAEE">emacs-lisp </span><span style="color:#CB7676">.</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#DBD7CAEE">python </span><span style="color:#CB7676">.</span><span style="color:#C99076"> t</span><span style="color:#666666">)</span></span>
<span class="line"><span style="color:#666666">(</span><span style="color:#B8A965">shell</span><span style="color:#CB7676"> .</span><span style="color:#C99076"> t</span><span style="color:#666666">)))</span></span></code></pre></div><p>With <code>TODO(t)</code> and friends in the keywords, <code>C-c C-t</code> stops cycling and shows a small menu instead: press <code>t</code>, <code>n</code>, <code>w</code>, <code>d</code> or <code>c</code> to jump straight to a state. If Python code blocks complain that <code>python</code> doesn't exist, your system probably calls it <code>python3</code>, and <code>(setq org-babel-python-command "python3")</code> fixes that.</p><h2 id="a-way-to-start">A way to start</h2><p>The worst way to learn Org is to set up everything on the first day. A gentler path:</p><ol>
<li value="1">
<strong>Week one: one file.</strong> Keep a single <code>notes.org</code>. Use headings, <code>TAB</code>, lists and <code>M-up</code>/<code>M-down</code>. Nothing else.</li>
<li value="2">
<strong>Week two: tasks.</strong> Add TODO, <code>C-c C-t</code>, SCHEDULED and DEADLINE. Open the agenda with <code>C-c a a</code> every morning.</li>
<li value="3">
<strong>Week three: capture.</strong> Set up one template and use <code>C-c c</code> for every idea, task and link that shows up.</li>
<li value="4">
<strong>Later, as needed.</strong> Tables when you need numbers, code blocks when you need to run something, export when you need to send something to someone.</li>
</ol><p>Every feature in Org is optional. A file with three headings and a TODO is already a perfectly good Org file.</p><h2 id="org-outside-emacs">Org outside Emacs</h2><p>Plain text travels well, and Org files are read by more programs than people expect:</p><ul>
<li value="1">GitHub, GitLab and Gitea render <code>.org</code> files on the web, the same way they render Markdown.</li>
<li value="2">Pandoc converts Org to and from Markdown, Word, HTML, EPUB and many other formats.</li>
<li value="3">On Android, <a href="https://github.com/orgzly-revived/orgzly-android-revived">Orgzly Revived</a> reads and edits Org files, with saved searches and reminders. It's on F-Droid.</li>
<li value="4">On iOS, <a href="https://apps.apple.com/app/id1238649962">beorg</a> does the same, with a to-do list and an agenda.</li>
<li value="5">In Neovim, <a href="https://github.com/nvim-orgmode/orgmode">nvim-orgmode</a> brings a good part of Org to Vim users.</li>
</ul><p>Put your <code>~/org</code> folder in whatever sync you already trust, and the same files follow you from the desktop to the phone.</p><h2 id="cheat-sheet">Cheat sheet</h2><p>Structure:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>TAB: fold or unfold the heading</span></span>
<span class="line"><span>S-TAB: fold or unfold the whole file</span></span>
<span class="line"><span>M-RET: new heading or list item</span></span>
<span class="line"><span>C-RET: new heading after the current section</span></span>
<span class="line"><span>M-left / M-right: promote or demote</span></span>
<span class="line"><span>M-S-left / M-S-right: promote or demote with children</span></span>
<span class="line"><span>M-up / M-down: move the section</span></span>
<span class="line"><span>C-x n s / C-x n w: narrow to the section / show everything again</span></span>
<span class="line"><span>C-c C-j: jump to a heading</span></span></code></pre></div><p>Tasks and dates:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c C-t: change the TODO state</span></span>
<span class="line"><span>S-left / S-right: cycle the state (or move a date)</span></span>
<span class="line"><span>S-up / S-down: change the priority (or the date part under the cursor)</span></span>
<span class="line"><span>C-c C-q: set tags</span></span>
<span class="line"><span>C-c C-s: schedule</span></span>
<span class="line"><span>C-c C-d: deadline</span></span>
<span class="line"><span>C-c .: insert a date</span></span>
<span class="line"><span>C-c !: insert an inactive date</span></span>
<span class="line"><span>C-c C-c: tick a checkbox, update a cookie, recalculate, run code (it does the right thing for where you are)</span></span>
<span class="line"><span>C-u C-c C-c: turn a list item into a checkbox</span></span></code></pre></div><p>Agenda, capture and links:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c a: open the agenda menu</span></span>
<span class="line"><span>C-c c: capture</span></span>
<span class="line"><span>C-c C-w: refile</span></span>
<span class="line"><span>C-c C-x C-a: archive</span></span>
<span class="line"><span>C-c l: store a link</span></span>
<span class="line"><span>C-c C-l: insert or edit a link</span></span>
<span class="line"><span>C-c C-o: open a link</span></span></code></pre></div><p>Clock, code and export:</p><div class="code-block"><pre class="shiki vitesse-dark" style="background-color:#121212;color:#dbd7caee" tabindex="0" data-lang="plain"><code><span class="line"><span>C-c C-x C-i: clock in</span></span>
<span class="line"><span>C-c C-x C-o: clock out</span></span>
<span class="line"><span>C-c C-,: insert a block</span></span>
<span class="line"><span>C-c ': edit a code block in its own buffer</span></span>
<span class="line"><span>C-c C-v t: tangle code blocks into files</span></span>
<span class="line"><span>C-c C-e: export menu</span></span></code></pre></div><h2 id="where-to-go-from-here">Where to go from here</h2><p>The Org manual is inside Emacs: press <code>C-h i</code>, then look for Org Mode in the list. It's long, well written and full of examples, and it's also online at <a href="https://orgmode.org/manual/">orgmode.org/manual</a>. <a href="https://orgmode.org/worg/">Worg</a>, the community wiki on the same site, has tutorials, configs and ideas from people who have been using Org for years.</p><p>And when you forget a key, remember the one key that never changes in Org: <code>C-c C-c</code> almost always does what you meant, wherever the cursor is.</p>]]></content>
    </entry>
</feed>
