Categories: sharing » writing

RSS - Atom - Subscribe via email

My Emacs writing experience

| writing, emacs, org

I've been enjoying reading people's responses to the Emacs Carnival July theme of writing experience. I know I don't need complicated tools to write. People can write in composition notebooks and on typewriters. But I have fun learning more about the Emacs text editor and tweaking it to support me. Writing is one of the ways I think, and I want to think better. I'll start with the kinds of things I write in my public and private notes, and then I'll think about Emacs specifically.

Types of notes

Text from sketch

What kinds of posts do I write? How? Improvements?

2025-07-25-05

  • Emacs News
    • why: collecting & connecting → fun!
    • how:
      • phone: Reddit: upvotes
      • YouTube: playlist
    • RSS
    • Mastodon: Scrape boosts?
    • Dedupe, categorize: classifier?
    • Blog
    • Mailing list
    • emacs.tv
    • emacslife.com/calendar
  • Bike Brigade newsletter
    • why: help out, connect
    • Reddit + X + Slack -> Slack canvas -> MailChimp
    • Need more regular last-min sweep
    • Copying from Slack sucks; Google Docs?
  • Tech notes
    • Why: figure things out, remember, share
    • code
    • literate programming: notes + code
    • debugger?
    • more notes?
    • thinking out loud?
  • Life reflections
    • Why: figure things out, remember
    • tangled thoughts
    • sketch: habit? more doodles
    • audio braindump
    • snippets on phone
    • learning to think
    • laptop: write
    • audio input?
    • themes, thoughts
      • LLM? reflection questions, topics to learn more about
  • Book notes
    • Why: study, remember, share
    • paper: draw while reading
    • e-book: highlight
      • quotes
    • sketch
      • smaller chunks?
    • blog
  • Monthly/yearly reviews
    • Why: plan, remember
    • phone: daily journal
    • tablet: draw moment of the day
    • phone: time records
    • Emacs: raw data
    • themes, next steps: LLM? reflection questions?
    • blog post

Emacs News

I put together a weekly list of categorized links about the interesting ways people use Emacs. This takes me about an hour or two each week. I enjoy collecting all these little examples of people's curiosity and ingenuity. Organizing the links into a list helps people find things they might be interested in and connect with other people.

I start by skimming r/emacs and r/orgmode on my phone, upvoting posts that I want to include. I also search YouTube and add videos to an Emacs News playlist. I review aggregated posts from Planet Emacslife. I have an Emacs Lisp function that collects all the data and formats them as a list, with all the items at the same level.

For Mastodon, I check #emacs search results from a few different servers. I have a keyboard shortcut that boosts a post and captures the text to an Org Mode file, and then I have another function that prompts me to summarize toots, defaulting to the title of the first link. I have more functions that help me detect duplicates and categorize links. I use ox-11ty to export the post to my blog, which uses the Eleventy static site generator. I also use emacstv.el to add the videos to the Org file I use for emacs.tv.

Some ways to improve this:

  • I probably have enough data that it might be interesting to learn how to write a classifier. On the other hand, regular expression matches on the titles get most of them correctly, so that might be sufficient.
  • YouTube videos are a little annoying to go through because of interface limitations and unrelated or low-effort videos. I can probably figure out something that checks the RSS feeds of various channels.

Bike Brigade newsletter

I also put together a weekly newsletter for Bike Brigade, which coordinates volunteer cyclists to deliver food bank hampers and other essentials. Writing this mostly involves collecting ideas from a number of social media feeds as well as the other volunteers in the community, putting together a draft, and then copying it over to Mailchimp. I'm still figuring out my timing and workflows so that I can stay on top of last-minute requests coming in from people on Slack, and so that I can repurpose newsletter items as updates in the Facebook group or maybe even a blog. If I set aside some regular time to work on things, like a Sunday morning sweep for last-minute requests, that might make it easier to work with other people.

Tech notes

I like coding, and I come up with lots of ideas as I use my computer. I enjoy figuring out workflow tweaks like opening lots of URLs in a region or transforming HTML clipboard contents. My Org files have accumulated quite a few. My main limiting factor here is actually sitting down to make those things happen. Fortunately, I have recently discovered that it's possible for me to spend an hour or two a day playing Stardew Valley, so I can swap some of that time for Emacs tweaking instead. Coding doesn't handle interruptions as well as playing does, but taking notes along the way might be able to help with that. I can jump to the section of my Org file with the ideas I wanted to save for more focus time, pick something from that screen, and get right to it.

Other things that might help me do this more effectively would be:

  • getting better at using my tools (debugger, documentation, completion, etc.),
  • taking the opportunity to plug in an external monitor, and
  • using my non-computer time to mull over the ideas so that I can hit the ground running.

I like taking notes at virtual meetups. I usually do this with Etherpad so that other people can contribute to the notes too. I don't have a real-time read-write Emacs interface to this yet (that would be way cool!), but I do have some functions for working with the Etherpad text.

Life reflections

When I notice something I want to figure out or remember, I use sketches, audio braindumps, or typing to start to untangle that thought. Sometimes I use all three, shifting from one tool to another depending on what I can do at the moment. I have a pretty comfortable workflow for converting sketches (Google Vision) or audio (OpenAI Whisper) to text so that I can work with it more easily, and I'm sure that will get even smoother as the technology improves. I switch from one tool to another as I figure out the shape of my thoughts.

Maybe I can use microblogging to let smaller ideas out into the world, just in case conversations build them up into more interesting ideas. I don't quite trust my ability to manage my GoToSocial instance yet (backups? upgrades?), so that might be a good reason to use a weekly or monthly review to revisit and archive those posts in plain text.

I've been reading my on this day list of blog posts and sketches more regularly now that it's in my feed reader. I like the way this helps me revisit old thoughts, and I've saved a few that I want to follow up on. It feels good to build on a thought over time.

I'd like to do more of this remembering and thinking out loud because memories are fleeting. Maybe developing more trust in my private journals and files will help. (Gotta have those backups!) Then I'll be more comfortable writing about the things we're figuring out about life while also respecting A+ and W-'s privacy, and I can post the stuff I'm figuring out about my life that I'm okay with sharing. I might think something is straightforward, like A+'s progress in learning how to swim. I want to write about how that's a microcosm of how she's learning how to learn more independently and my changing role in supporting her. Still, she might have other opinions about my sharing that, either now or later on. I can still reflect on it and keep that in a private journal as we figure things out together.

Even though parenting takes up most of my time and attention at the moment, it will eventually take less. There are plenty of things for me to learn about and share outside parenting, like biking, gardening, and sewing. I've got books to read and ideas to try out.

I'm experimenting with doing more writing on my phone so that I can get better at using these little bits of time. Swiping letters on a keyboard is reasonably fast, and the bottleneck is my thinking time anyway. I use Orgzly Revived so that Syncthing can synchronize it with my Org Mode files on my laptop when I get back home. There are occasional conflicts, but since I mostly add to an inbox.org when I'm on my phone, the conflicts are usually easy to resolve.

Adding doodles to my reflections can make them more fun. I can draw stick figures from scratch, and I can also trace my photos using the iPad as a way to add visual anchors and practise drawing. If I get the hang of using a smaller portion of my screen like the way I used to draw index cards, that might make thoughts more granular and easier to complete.

When I write on my computer, I often use writeroom-mode so that things feel less cluttered. I like having big margins and short lines. I have hl-line-mode turned on to help me focus on the current paragraph. This seems to work reasonably well.

2025-07-26_00-33-44.png
Figure 1: Screenshot showing writeroom-mode and hl-line-mode

Monthly and yearly reviews

I like the rhythm of drawing daily moments and keeping a web-based journal of brief descriptions of our day. I like how I've been digging into them deeper to reflect on themes. The monthly drawings and posts make it easier to review a whole year. Maybe someday I'll get back to weekly reviews as well, but for now, this is working fine.

My journal entries do a decent job of capturing the facts of our days: where we went, what we did. Maybe spending more time writing life reflections can help me capture more of what goes on in my head and what I want to learn more about.

Book notes

I draw single-page summaries of books I like because they're easier to remember and share. E-books are convenient because I can highlight text and extract that data even after I've returned the book, but I can also retype things from paper books or use the text recognition feature on my phone camera. I draw the summaries on my iPad using Noteful, and then I run it through my Google Vision workflow to convert the text from it so that I can include it in a blog post.

The main limiting factor here is my patience in reading a book. There are so many other wonderful things to explore, and sometimes it feels like books have a bit of filler. When I have a clear topic I'm curious about or a well-written book to enjoy, it's easier to study a book and make notes.

Emacs workflow thoughts

Aside from considering the different types of writing I do, I've also been thinking about the mechanics of writing in Emacs. Sanding down the rough parts of my workflow makes writing more enjoyable, and sometimes a small tweak lets me squeeze more writing into fragments of time.

There are more commands I want to call than there are keyboard shortcuts I can remember. I tend to use M-x to call commands by name a lot, and it really helps to have some kind of completion (I use vertico) and orderless matching.

I'm experimenting with more voice input because that lets me braindump ideas quickly on my phone. Long dictation sessions are a little difficult to edit. Maybe shorter snippets using the voice input mode on the phone keyboard will let me flesh out parts of my outline. I wonder if the same kind of quick input might be handy on my computer. I'm trying out whisper.el with my Bluetooth earbuds. Dictating tends to be stop-and-go, since I feel self-conscious about dictating when other people are around and I probably only have solo time late at night.

Misrecognized words can be annoying to correct on my phone. They're much easier to fix on my computer. Some corrections are pretty common, like changing Emax to Emacs. I wrote some code for fixing common errors (my-subed-fix-common-errors), but I don't use this often enough to have it in my muscle memory. I probably need to tweak this so that it's a bit more interactive and trustworthy.

When I see a word I want to change, I jump to it with C-s (isearch-forward) or C-r (isearch-backward), or I navigate to it with M-f (forward-word). I want to get the hang of using Avy because of Karthik's awesome post about it. That post is from 2021 and I still haven't gotten used to it. I probably just need deliberate practice using the shortcut I've mapped to M-j (avy-goto-char-timer). Or maybe I just don't do this kind of navigation enough yet to justify this micro-optimization (no matter how neat it could be), and isearch is fine for now.

Sometimes I want to work with sentences. expand-region is another thing I want to get used to. I've bound C-= to er/expand-region from that package. Then I should be able to easily kill the text and type a replacement or move things around. In the meantime, I can usually remember to use my keyboard shortcut of M-z for avy-zap-up-to-char-dwim for deleting something.

Even in vanilla Emacs, there's so much that I think I'll enjoy getting the hang of. oantolin's post on his writing experience helped me learn about M-E, which marks the region from the point to the end of the sentence and is a natural extension from M-e. Similarly, M-F selects the next word. I could use this kind of shift-selection more. I occasionally remember to transpose words with M-t, but I've been cutting and pasting sentences when I could've been using transpose-sentences all this time. I'm going to add (keymap-global-set "M-T" #'transpose-sentences) to my config and see if I remember it.

I like using Org Mode headings to collapse long text into a quick overview so I can see the big picture, and they're also handy for making tables of contents. It might be neat to have one more level of overview below that, maybe displaying only the first line of each paragraph. In the meantime, I can use toggle-truncate-lines to get that sort of view.

If I'm having a hard time fitting the whole shape of a thought into my working memory, I sometimes find it easier to work with plain list outlines that go all the way down to sentences instead of working with paragraphs. I can expand/collapse items and move them around easily using Org's commands for list items. In addition, org-toggle-item toggles between items and plain text, and org-toggle-heading can turn items into headings.

I could probably write a command that toggles a whole section between an outline and a collection of paragraphs. The outline would be a plain list with two levels. The top level items would be the starting sentences of each paragraph, and each sentence after that would be a list item underneath it. Sometimes I use actual lists. Maybe those would be a third level. Then I can use Org Mode's handy list management commands even when a draft is further along. Alternatively, maybe I can use M-S-left and M-S-right to move sentences around in a paragraph.

Sometimes I write something and then change my mind about including it. Right now, I tend to either use org-capture to save it or put it under a heading and then refile it to my Scraps subtree, but the palimpsest approach might be interesting. Maybe a shortcut to stash the current paragraph somewhere…

I use custom Org link types to make it easier to link to topics, project files, parts of my Emacs configuration, blog posts, sketches, videos, and more. It's handy to have completion, and I can define how I want them to be exported or followed.

Custom Org link types also let me use Embark for context-sensitive actions. For example, I have a command for adding categories to a blog post when my cursor is on a link to the post, which is handy when I've made a list of matching posts. Embark is also convenient for doing things from other commands. It's nice being able to use C-. i to insert whatever's in the minibuffer, so I can use that from C-h f (describe-function), C-h v (describe-variable), or other commands.

I also define custom Org block types using org-special-block-extras. This lets me easily make things like collapsible sections with summaries.

I want to get better at diagrams and charts using things like graphviz, mermaidjs, matplotlib, and seaborn. I usually end up searching for an example I can build on and then try to tweak it. Sometimes I just draw something on my iPad and stick it in. It's fine. I think it would be good to learn computer-based diagramming and charting, though. They can be easier to update and re-layout when I realize I've forgotten to add something to the graph.

Figuring out the proper syntax for diagrams and charts might be one of the reasonable use cases for large-language models, actually. I'm on the fence about LLMs in general. I sometimes use claude.ai for dealing with the occasional tip of the tongue situation like "What's a word or phrase that describes…" and for catching when I've forgotten to finish a sentence. I don't think I can get it to think or write like me yet. Besides, I like doing the thinking and writing.

I love reading about other people's workflows. If they share their code, that's fantastic, but even descriptions of ideas are fine. I learn so many things from the blog posts I come across on Planet Emacslife in the process of putting together Emacs News. I also periodically go through documentation like the Org Mode manual or release notes, and I always learn something new each time.

This post was really hard to write! I keep thinking of things I want to start tweaking. I treat Emacs-tweaking as a fun hobby that sometimes happens to make things better for me or for other people, so it's okay to capture lots of ideas to explore later on. Sometimes something is just a quick 5-minute hack. Sometimes I end up delving into the source code, which is easy to do because hey, it's Emacs. It's comforting and inspiring to be surrounded by all this parenthetical evidence of other people's thinking about their workflows.

Each type of writing helps me with a different type of thinking, and each config tweak makes thoughts flow more smoothly. I'm looking forward to learning how to think better, one note at a time.

Check out the Emacs Carnival July theme: writing experience post for more Emacs ideas. Thanks to Greg Newman for hosting!

View org source for this post

Finding the shape of my thoughts

| emacs, org, writing

Text from sketch

Finding the shape of my thoughts 2025-07-23-02

I have a hard time following a thought from beginning to end.

Some people are like this and have figured out things that work well for them.

Challenges:

  • too much or not enough
  • one more thing; rabbit holes
  • dangling thoughts

iPad

  • shape of thought
    • topics?
    • enough?
    • flow?
  • metaphors, visual frameworks?
  • zooming in? links?
  • text boxes?

Phone

  • outline, snippets, placeholders
  • outline?
  • short dictation?
  • Keyboard?

Laptop

  • fleshing out: code, links, etc.
  • Zettelkasten?
  • editing audio braindump?
  • managing idea pipeline?
  • leave TODOS, mark them

Overall:

  • Develop thoughts in conversation
  • Use the constraints
  • Get the ball rolling

I want to write more. Writing better can follow, with practice and reflection. But writing is challenging. Coming up with ideas is not the hard part. It's finishing them without getting distracted by the hundred other ideas I come up with along the way. I have a hard time following one thought from beginning to end. My mind likes to flit around, jumping from one idea to another. Even when I make an outline, I tend to add to one section, wander over to another, come back to the first, get very deep into one section and decide it's probably its own blog post, and so on. Sometimes I want to say too much to fit into a blog post. Sometimes I start writing and find that I don't have enough to say yet. Sometimes I keep getting distracted by one more thing I want to do before I feel like I can finish the post. Sometimes an idea turns out to be a deep rabbit hole. Sometimes I can rein in those thoughts by using TODOs and next steps and somedays, but then I have all these threads left dangling and it doesn't quite feel right.

Fortunately, other people have figured out how to work with things like this. Roland Allen shares an example in The Notebook: A History of Thinking on Paper (2023):

P117. More commonly, [Leonardo da Vinci] expresses annoyance at his own distractability or perceived lack of progress. "Alas, this will never get anything done" is a theme that recurs in several notebooks.

Asked about the experience of looking at the "spine-tingling" notebooks, [Martin] Kemp employs strikingly kinetic language. "As material objects, they have an extraordinary intensity, little notebooks with this pretty tiny writing, done at great speed, great urgency, a kind of desperate intensity, when something else crowds in he has to jot it down, he goes back to the original thought, he gets diverted, he comes back to that page and will write some more… it's a very manic business."

My life is much smaller scale, but it's nice to know that other people have figured out or are figuring out how to work with how they are. For example, I've been drafting a post for July's Emacs Carnival theme of writing experience. Along the way, I found myself adding my blog posts as a consult-omni source, using that to add URLs to link placeholders, and writing this post about non-linearity. I'm very slowly learning to break those up into their own posts, or maybe even just save the idea as a TODO so that I can finish the thing that I'm writing before I get distracted by figuring out something else. It's easier to work with the grain than against it, so I follow wherever my curiosity leads, and then figure out what chunks I can break into posts.

I'm also coming to terms with the fact that I don't know what I'm writing until I write it and tweak it. No Athena springing forth fully-formed. The ideas develop in conversation: me with my sketches and text, and if I'm lucky, other people too. Sometimes there isn't enough there yet, so I need to put the idea aside for now. Sometimes there's too much I want to say, so I need to select things to focus on.

When I have an idea I want to write about, I like to start with drawing in Noteful on my iPad: sometimes a mind map, sometimes just words and phrases scattered on a page until I figure out which things are close to each other. I can select things with a lasso and move them closer together, and I can use a highlighter to choose things to focus on. This helps me get a sense of what I want to write about and what examples I want to use. Then I can take a step back and figure out the order that makes sense for the post.

My starting sketch for this post:

Text from sketch

Non-linear writing 2025-07-23-01

  • 1. I have a hard time following one thought from beginning to end.
    • My mind likes to flit around.
    • Other people have figured out how
  • 2. Challenges:
    • trying to cram in too much
    • one more thing
    • yak-shaving / rabbitholes
    • dangling threads
  • 3. iPad
    • Map
    • enough
    • not dense
    • starting points
    • Sketch
    • Order
    • crossing out?
    • Structure?
    • rough sketch vs shareable
    • hyperlinks?
      • Zoom in
        • finer pen, actual zoom?
        • This is as small as it gets. Extra details?
    • Bluetooth keyboard?
    • Beorg, Plain Org?
    • Airdroid or hotspot?
    • visual frameworks, David Gray
    • I'm experimenting with using Noteful's text boxes so that I can quickly dictate the thought
  • 4. Phone
    • Snippets
    • short dictation?
    • outline - collapsible?
    • Sometimes I only have my phone with me. I can make a quick outline in orgzly revived and then fill in paragraphs jumping around as needed. Sometimes I feel like I'm going to lose track of the dangling threads, especially if they're in the middle of a paragraph so maybe a Todo marker might be good for that.
    • Outline
  • 5. Laptop
    • break out smaller chunks into their own posts.
    • leave TODOS
    • Zettelkasten
    • mention Emacs Conf talk about writing; also org-roam
  • audio braindump
    • tangled
    • editing?
    • needs computer for now
    • LLM?
    • Shorter is prob. more useful
  • drawing metaphor?
    • painting?
    • mark-making
    • bounds, shape
    • gradually fill in
  • move ideas for improvement to the different sections

Sometimes ideas peter out at this stage, when I find that I don't have much to say yet about it. I organize my Noteful notes by month, so I just leave the unfinished sketch there. I could probably tag it to make it findable again, but I usually end up moving on to other thoughts instead. If I want to revisit an idea later on, it's often easier to just make a new map. There are so many ideas I can explore, so it's good to quickly find out when I don't have enough to say about something. It might make more sense to me later on.

If I can figure out the rough shape of an idea and I feel like I have enough thoughts to fill it with, then it's time to figure out words. Swiping on an onscreen keyboard is more comfortable on my phone than on the iPad, although maybe that's just a matter of getting used to it. If I've developed the idea enough to have a clear flow, I can write the outline without referring to my sketch. If I happen to have a flat surface like a table, I can write while looking at my drawing. Once I have an outline on my phone, I can fill it in with paragraphs.

Sometimes it's easier for me to dictate than to type, like when I'm watering the plants. I use OpenAI Whisper to transcribe the recordings. The speech recognition is pretty accurate, but I have a lot of false starts, tangents, and rephrasing. My thoughts are rough and tangled, tripping over each other on their way out, but saying them out loud gets them down into a form I can look at. I still need to do a fair bit of work to clean up the text and organize it into my outline. Some people use large-language models to organize raw transcripts, but I haven't quite figured out a good prompt that turns a wall of raw text into something that's easy to include in my draft while retaining my voice. At the moment, I'd rather just manually go over my transcript for ideas I want to include and phrasings I might want to keep. As I massage the braindump into a post, I notice other things I want to add or rephrase. Maybe I'll get the hang of using voice input mode to dictate shorter snippets so that I can do it on my phone or iPad instead of needing computer time.

When I'm ready to expand these fragments into full posts, it's easiest to write on the computer, especially if I want to look up documentation or write code. My Orgzly Revived notes generally synchronize seamlessly with Org Mode in Emacs via Syncthing, aside from the occasional sync conflict that I need to resolve. Then I can build on whatever I started jotting down on my phone. Since I type quickly, thinking is the real bottleneck. If I've thought about things enough through my sketches or phone drafts, writing on my computer goes faster.

I find it easier to assemble a thought out of smaller snippets than to write from scratch, which is why Zettelkasten appeals to me. I still want to figure out some kind of approximate search, or even an exact search that can check Org entry text in a reasonable way. (Maybe org-ql…) My notes are not nearly as organized as people's org-roam constellations, but I'm starting to be able to pull together snippets of drafts, quotes from books, links to previous blog posts, and things I've come across in my reading.

Some ideas stall at this stage, too. M-x occur for "^\* " shows 65 top-level headings in my posts.org drafts. Sometimes it's because I've run into something I haven't figured out yet. Sometimes it's because the thoughts are still tangled. Sometimes it's because I've gotten distracted by other things, or a different approach to the same topic. I generally work on the more recent ones that are still in my mind. I also have a tree-map visualization that gives me a sense of the heft of each draft, in case the accumulation of words helps nudge me to finish it. It's okay for thoughts to take a while.

2025-07-23_20-18-40.png
Figure 1: Treemap visualization of my posts.org

So my iPad is for sketching out the thought, my phone is for writing on the go, and my computer is for fleshing it all out. How could I make this better?

  • iPad:
    • What if I use more structure, visual frameworks, or metaphors, instead of starting from a totally blank page? That can help suggest things to think about, like the way a 2x2 matrix can help organize contrasts.
    • I can zoom in and write with a thin stroke to add more detail. If I need even more space, I can link to a separate page.
    • I can add textboxes and use voice input to quickly capture fragments of ideas as I draw.
  • Phone:
    • I can explore the outline tools of Beorg, Plain Org, or Orgzly Revived to see if I can get the hang of using them when I'm away from my computer.
    • I can try the voice input on my phone. To keep the flow going, I need to resist the urge to correct misrecognized words as long as things are somewhat understandable.
    • Maybe I can try bringing a Bluetooth keyboard to playdates where I'm likely to be near a table.
  • Writing:
    • I can run more ideas through my audio braindumping process so that I can improve my workflow.
    • I can use Org Mode TODO states or tags to manage my idea pipeline so that I can keep track of posts that are almost there.
    • I can be more ruthless about parking an idea as a TODO or a next step instead of feeling like I need to go write that post before I can finish this one. This might also help me write in smaller chunks.

Even if I have to rewrite chunks as I figure things out, that's not a waste. That's just part of how thoughts develop. I'm constrained by the tools that I use and the fragments of time that I have, but I can use those constraints to help me break things down into manageable pieces. If I take advantage of those little bits of time to get the ball rolling, writing at the computer becomes much easier and more fun. This is the kind of brain I've got, and I enjoy learning more about working with it.

View org source for this post

Working on the plumbing in a small web community

| connecting, emacs, blogging

The IndieWeb Carnival prompt for May is small web communities. I've been exploring some thoughts on how a little effort goes a long way to connecting a community. Sometimes I think of it as working on the plumbing so that ideas can flow more smoothly. It feels a little different from the direct contribution of knowledge or ideas. I also want to connect with other people who do this kind of thing.

Emacs is a text editor that has been around since the 1970s. It's highly programmable, so people have come up with all sorts of ways to modify it to do what they want. It's not just for programmers. My favourite examples include novelists and bakers and musicians who use Emacs in unexpected ways. Because Emacs is so flexible, community is important. The source code and documentation don't show all the possible workflows. As people figure things out by themselves and together, more possibilities open up.

I love tweaking Emacs to help me with different things I want to do, and I love learning about how other people use it too. I've been sharing my notes on Emacs on this blog since 2001 or so. In 2015, as I was getting ready to become a parent, I knew I was going to have much less time and focused attention, which meant less time playing with Emacs. Fortunately, around that time, John Wiegley (who was one of the maintainers of Emacs at the time) suggested that it would be helpful if I could keep an eye on community updates and summarize them. This worked well with the fragmentation of my time, since I could still speed-read updates and roughly categorize them.

Text from sketch

Community plumbing

You don't have to fill the pipes all by yourself. Just help things flow.

I want to share some of the things we're doing in the Emacs community so that I can convince you that building plumbing for your community can be fun, easy, and awesome. This is great because enthusiasm spreads.

virtuous cycle

  • Other places: YouTube, Reddit, HN, lobste.rs, Mastodon, PeerTube, mailing lists….
  • Blog aggregator
    • Planet Emacs Life (uses Planet Venus) - update: [2025-05-31 Sat] I wrote my own RSS feed aggregator instead.
  • Newsletter: Emacs News, 1-2 hours a week
    • summarize & group
    • announce calendar events
  • User groups
    • [often use Emacs News to get conversations going]
  • iCal & Org files: Emacs Calendar
  • Conference
    • EmacsConf: < USD 50 hosting costs + donated server + volunteer time

Tips:

  • Make it fun for yourself.
  • Build processes and tools.
  • Let people help

2024-01-31-05

Some more notes on the regular flows built up by this kind of community plumbing:

Daily: Lots of people post on reddit.com/r/emacs and on Mastodon with the #emacs hashtag. I also aggregate Emacs-related blog posts at planet.emacslife.com, taking over from planet.emacsen.org when Tess had DNS issues. There are a number of active channels on YouTube and occasionally some on PeerTube instances as well. I don't need to do much work to keep this flowing, just occasionally adding feeds to the aggregator for planet.emacslife.com.

Weekly: I collect posts from different sources, remove duplicates, combine links talking about the same thing, categorize the links, put them roughly in order, and post Emacs News to a website, an RSS feed, and a mailing list. This takes me maybe 1.5 hours each week. It's one of the highlights of my week. I get to learn about all sorts of cool things.

Weekly seems like a good rhythm for me considering how active the Emacs community is. Daily would be too much time. Monthly would lead to either too long of a post or too much lost in curation, and the conversations would be delayed.

Sometimes I feel a twinge of envy when I check out other people's newsletter posts with commentary or screenshots or synthesis. (So cool!) But hey, I'm still here posting Emacs News after almost ten years, so that's something. =) A long list of categorized links fits the time I've got and the way my mind works, and other people can put their own spin on things.

Monthly: There are a number of Emacs user groups, both virtual and in-person. Quite a few of them use Emacs News to get the discussion rolling or fill in gaps in conversation, which is wonderful.

Some meetups use meet.jit.si, Zoom, or Google Meet, but some are more comfortable on a self-hosted service using free software. I help by running a BigBlueButton web conferencing server that I can now automatically scale up and down on a schedule, so the base cost is about 60 USD/year. Scaling it up for each meetup costs about USD 0.43 for a 6-hour span. It's pretty automated now, which is good because I tend to forget things that are scheduled for specific dates. My schedule still hasn't settled down enough for me to host meetups, but I like to drop by once in a while.

Yearly: EmacsConf is the one big project I like to work on. It's completely online. It's more of a friendly get-together than a formal conference. I have fun trying to fit as many proposed talks as possible into the schedule. We nudge speakers to send us recorded presentations of 5-20 minutes (sometimes longer), although they can share live if they want to. A number of volunteers help us caption the videos. Each presentation is followed by Q&A over web conference, text chat, and/or collaborative document. Other volunteers handle checking in speakers and hosting the Q&A sessions.

It's a lot of fun for surprisingly little money. For the two-day conference itself, the website hosting cost for EmacsConf 2024 was about USD 56 and our setup was able to handle 400 viewers online (107 max simultaneous users in various web conferences).

EmacsConf takes more time. For me, it's about 1.5 hours a day for 4 months, but I think mostly that's because I have so much fun figuring out how to automate things and because I help with the captions. Lots of other people put time into preparing presentations, hosting Q&A, participating, etc. It's worth it, though.

I like doing this because it's a great excuse to nudge people to get cool stuff out of their head and into something they can share with other people, and it helps people connect with other people who are interested in the same things. Some Q&A sessions have run for hours and turned into ongoing collaborations. I like turning videos into captions and searchable text because I still don't have the time/patience to actually watch videos, so it's nice to be able to search. And it's wonderful gathering lots of people into the same virtual room and seeing the kind of enthusiasm and energy they share.

So yeah, community plumbing turns out to be pretty enjoyable. If this resonates with you, maybe you might want to see if your small web community could use a blog aggregator or a newsletter. Doesn't have to be anything fancy. You could start with a list of interesting links you've come across. I'm curious about what other people do in their communities to get ideas flowing!

Related: the community plumbing section of my blog post / livestream braindump.

View org source for this post

Adding subheadings and sketches to my blog page navigation

Posted: - Modified: | 11ty, blogging

[2025-04-04 Fri]: Fixed link to onThisPage.cjs. Thanks to John Rakestraw for pointing it out!

Assumed audience:

  • My future self, when I'm trying to figure out where to change things if I want to implement something similar
  • People who like tweaking their blog's CSS, especially if they also use Org Mode or 11ty

Headings help us make sense of longer blog posts. Heading links are like signposts letting you know what's ahead and where you can take a shortcut to get to what you're interested in.

Headings are useful for me too. Sometimes I browse my blog and come across things I've completely forgotten writing about, so the headings can help me remember without having to reread long posts. If I use headings more often, I might be able to work with bigger chunks of thoughts. If I can work with bigger chunks of thoughts, then maybe I can think about more things that are hard to fit within the limits of my working memory. Making headings more navigable also means I might not have to worry about the tangents I go on and the number of different thoughts I try to smoosh together, if people can jump straight to the parts that sound relevant to them.

I particularly like the way Karthik uses a sticky table of contents for long blog posts like The Emacs Window Management Almanac | Karthinks. I also like the way the Read the Docs Sphinx Theme displays a nested table of contents on the left side on wide screens. I use org-html-themes to export Org Mode files with that theme when I want to be fancy, like my Emacs configuration (usually works, although sometimes my config is broken).

The last time I tinkered with my webpage margins, I put my "On this page" list on the left side and the blog post headings (if any) on the right side, mostly because it was easy to do. I just changed the margin and float attributes of the element with the subheadings. I'd like to clear up more space for potential sidenotes or doodles, though. This time, I experimented with nesting the blog navigation inside the "On this page" navigation on the left side.

2025-04-02_14-04-30.png
Figure 1: A screenshot of my blog showing the nested links

Here's how I did it:

Org Mode: where I start writing

I use Org Mode's table of contents directive to include a table of contents in blog posts. I wrap it inside a sticky-toc block to indicate when I want it to be part of the sticky table of contents on my blog. In Org Mode, the syntax looks like this:

#+begin_sticky-toc
#+TOC: headlines 2 local
#+end_sticky-toc

I put it in a yasnippet so that I don't have to remember it. I just type tocs (for TOC, sticky) and then press TAB to complete it.

11ty static site generator: on this page

After exporting individual HTML files from Org Mode, I turn them into my blog using the 11ty static site generator. To simplify my archive pages, I have an onThisPage shortcode which lists the posts on that page. I changed it to include the sticky-toc contents from the items' templateContent attributes.

const { JSDOM } = require('jsdom');

module.exports = function (eleventyConfig) {
  function formatPostLine(item, index) {
    let subtoc = '';
    if (item.templateContent?.match(/sticky-toc/)) {
      const doc = new JSDOM(item.templateContent).window.document;
      const sub = doc.querySelector('.sticky-toc ul, .sticky-toc div, .sticky-toc-after-scrolling div');
      if (sub) {
        if (sub.querySelector('.panzoom')) {
          console.log('remove panzoom');
          sub.querySelector('.panzoom').classList.remove('panzoom');  // don't do panzoom for now
        }
        sub.classList.remove('panzoom');
        subtoc = sub.outerHTML;
      }
    }
    return `<li><a class="toc-link" data-index="index${index}" href="${item.url}">${item.data.title}</a>${subtoc}</li>`;
  }
  eleventyConfig.addShortcode('onThisPage', function (list) {
    return `<nav class="on-this-page">
On this page:
<ul>
${list.map(formatPostLine).join("\n")}
</ul>
</nav>`;
  });
};

And then there's a bunch of CSS in assets/css/style.css:

CSS
/* tables of contents */
.on-this-page > ul > li > ul, .on-this-page > ul > li > div { display: none }

@media only screen and (width >= 95em) {
    html, body { overflow-x: unset; }

    .sticky-toc, .sticky-left, .sticky-right {
        font-size: var(--fs-sm);
        width: calc((100vw - var(--body-max-width) - 5rem)/2);
        position: sticky;
        max-height: calc(100vh - 2rem);
        overflow-y: auto;
        scroll-behavior: smooth;
        background: var(--modus-bg-main);
        top: 0;
        padding: 1rem;
    }

    article .sticky-toc {
        display: none
    }

    .single-post article .sticky-toc {
        display: block;
    }

    .sticky-toc, .sticky-left, .single-post article .sticky-toc {
        margin-left: calc((-100vw + var(--body-max-width))/2);
        float: left;
    }

    .sticky-right {
        margin-right: calc((-100vw + var(--body-max-width))/2);
        float: right;
    }

    /* Hide the TOCs for non-active posts, but only if JS is enabled */
    .js .on-this-page > ul > li > ul, .js .on-this-page > ul > li > div { display: none }
    .on-this-page > ul > li.post-active > ul, .on-this-page > ul > li.post-active > div { display: block }

    .active { background-color: var(--modus-bg-tab-bar) }

    .sticky-toc svg .active rect {
        fill: var(--modus-bg-tab-bar) !important;
        fill-opacity: 1 !important;
        mix-blend-mode: darken;
        stroke-dash-array: unset !important;
        stroke-width: 4px;
    }

    .link-to-nonsticky-toc {
        display: none
    }
}


I also have some Javascript to highlight the active post and show the subheadings for it in assets/js/misc.js.

Javascript
/* Table of contents */

function stickyTocAfterScrolling() {
  const elements = document.querySelectorAll('.single-post .sticky-toc-after-scrolling');
  let lastScroll = window.scrollY;

  elements.forEach(element => {
    const clone = element.cloneNode(true);
    clone.setAttribute('class', 'sticky-toc');
    clone.querySelector('.panzoom')?.classList.remove('panzoom');
    element.parentNode.insertBefore(clone, element.nextSibling);
  });

  const observer = new IntersectionObserver(
    (entries) => {
      const currentScroll = window.scrollY;
      const scrollingDown = currentScroll > lastScroll;
      lastScroll = currentScroll;

      entries.forEach(entry => {
        const element = entry.target;
        const clone = cloneMap.get(element);

        if (!entry.isIntersecting && scrollingDown) {
          clone.setAttribute('class', 'sticky-toc');
          clone.style.display = 'block';
        } else if (entry.isIntersecting && !scrollingDown) {
          element.style.visibility = 'visible';
          clone.style.display = 'none';
        }
      });
    },
    {
      root: null,
      threshold: 0,
      rootMargin: '-10px 0px 0px 0px'
    }
  );

  elements.forEach(element => {
    observer.observe(element);
  });

  window.addEventListener('resize', () => {
    elements.forEach(element => {
      const clone = cloneMap.get(element);
      if (clone.style.display != 'none') {
        // reset didn't seem to work
        svgPanZoom(clone.querySelector('svg')).destroy();
        addPanZoomToElement(clone.querySelector('svg'));
      }
    });
  }, { passive: true });
}

stickyTocAfterScrolling();

function scrollToActiveTocLink() {
  const activeLink = document.querySelector('.sticky-toc .active');
  const tocContainer = document.querySelector('.sticky-toc');
  if (!activeLink || !tocContainer) return;
  const tocRect = tocContainer.getBoundingClientRect();
  const linkRect = activeLink.getBoundingClientRect();
  if (linkRect.top < tocRect.top || linkRect.bottom > tocRect.bottom) {
    const scrollPosition = linkRect.top + tocContainer.scrollTop -
                          (tocRect.height / 2) + (linkRect.height / 2);
    tocContainer.scrollTo({
      top: scrollPosition,
      behavior: 'smooth'
    });
  }
}
function getVisibleArticle() {
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
  return [...document.querySelectorAll('article')].find((article) => {
    const rect = article.getBoundingClientRect();
    const visibleTop = Math.max(0, rect.top);
    const visibleBottom = Math.min(viewportHeight, rect.bottom);
    const visibleHeight = Math.max(0, visibleBottom - visibleTop);
    return visibleHeight > 0; // find the first visible one
  });
}

function handleActiveTOCLink() {
  const updateActive = function(links, active) {
    const activeFragment = active.includes('#') ?
          active.substring(active.indexOf('#')) : '';
    links.forEach(link => {
      const href = link.getAttribute('href');
      if (href.includes(window.location.origin)) {
        link.classList.toggle('active', href == active)
      } else if (href.startsWith('#')) {
        link.classList.toggle('active', href == activeFragment);
      }
    });
  };
  const posts = document.querySelectorAll('.post');
  const tocLinks = document.querySelectorAll('.on-this-page .toc-link');
  const options = {
    root: null,
    rootMargin: '-20% 0px -70% 0px',
    threshold: 0
  };
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const id = entry.target.id;
        const link = document.querySelector(`.toc-link[data-index="${id}"]`);
        document.querySelectorAll('.sticky-toc .active').forEach((o) => o.classList.remove('active'));
        document.querySelectorAll('.post-active').forEach((o) => o.classList.remove('post-active'));
        if (link) {
          link.classList.add('active');
          const item = link.closest('li');
          item.classList.add('post-active');
        }
      }
    });
    scrollToActiveTocLink();
  }, options);
  posts.forEach((post) => { observer.observe(post); });

  const stickyTocLinks = document.querySelectorAll('article .sticky-toc a, .on-this-page a');
  const postTocObserver = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const id = entry.target.id;
        const url = window.location.origin + window.location.pathname + '#' + id.replace(/^outline-container-/, '');
        updateActive(stickyTocLinks, url);
      }
    });
    scrollToActiveTocLink();
  }, options);

  document.querySelectorAll('article .sticky-toc, article .sticky-toc-after-scrolling').forEach((toc) => {
    const post = toc.closest('article');
    if (post) {
      post.querySelectorAll('.outline-2, .outline-3').forEach((section) => { postTocObserver.observe(section) });
    }
  });
  const visible = getVisibleArticle();
  const id = visible?.id;
  if (id) {
    const activeLink = document.querySelector(`.toc-link[data-index="${id}"]`);
    if (activeLink) {
      activeLink.classList.add('active');
      activeLink.closest('li').classList.add('post-active');
      scrollToActiveTocLink();
    }
  }
}
handleActiveTOCLink();

I can use a sketch as a map, too

I sometimes want to use sketchnotes as overviews, especially if I've added hyperlinks to them. I used to make the images show up on the right side, but now I want them to show up in the left-side navigation instead. Also, I wanted any links to headings to automatically get recoloured as I scroll to that heading.

2025-04-02-scrolling-svg.gif
Figure 2: Animated GIF showing how the SVG highlights change as you scroll down

I added a special case to the handleActiveTOCLink function to handle anchor hyperlinks (just #anchor) in the SVG. It probably makes sense to make those absolute URLs, which means slightly changing my workflows for hyperlinking SVGs and writing about sketches.

So on both the category page (ex: the Hyperlinking SVGs entry in category - drawing, which might have moved off the first page of results if you're reading this far in the future) and the single-post page (ex: Hyperlinking SVGs), there's a full-sized version of the image in the main blog post, and then a small copy of it in the margin on the left. The sidebar copy is probably too small to read, but it might be enough to get a sense of spatial relationships, and the links also have title attributes that are displayed as tooltips when you hover.

2025-04-03_14-28-48.png
Figure 3: Screenshot of small image in sidebar on the single post page

I use Javascript to duplicate the image and make a small, sticky version because I haven't quite figured out how to properly make it sticky when off-screen with just CSS. Even my JS feels a little tangled. Maybe this would be a good excuse to learn about web components; someone's probably figured out something polished.

I'm curious about using more drawings to anchor my thinking and structure my blog posts.

Progressive enhancement

Some people read my blog using EWW (the Emacs Web Wowser, of course), so I want my blog to be reasonable even without CSS and JS.

A number of people read my blog without Javascript enabled. I installed the Firefox extension Script Switch so that I can test my blog with and without Javascript whenever I remember.

I sometimes look up my blog posts on my phone and there's no space for any of this fanciness there, so it'll only kick in on large screens. My CSS file is littered with various breakpoints I've cargo-culted over the years and I should simplify it at some point. At the moment, if it looks fine on my Lenovo P52, I'm happy.

Other ideas and next steps

Theoretically, the right margin is now available for sidenotes, so I might be able to look at ox-tufte and Eleventufte and get something going. Then I'll have a way to add small notes that are shorter than a paragraph. Longer tangents can go in a details/summary element instead, although I have it on good authority that one can write at length in footnotes. I love the footnotes in the Bartimaeus series, and apparently there are quite a few books where the footnotes are part of the storytelling.)

It might be nice to let tables extend into the right sidebar when I know I won't have a doodle nearby. Incidentally, Sidenotes In Web Design · Gwern.net uses breadcrumbs in the left sidebar instead of a table of contents, so there's more space for tables and sidenotes.

I thought about using CSS breakpoints so that on a medium-sized screen, we can have the left sidebar even if there's no space for something on the right. I haven't gotten around to experimenting with it yet, though. Besides, I don't know yet if I want to prioritize the stuff I want in the right sidebar (side notes, doodles) over fairly-static navigation.

As I mentioned, it might be handy to tweak my SVG linking workflow to use absolute URLs.

Sometimes I look up my notes within Emacs, but surprisingly often, I look them up on the Web. Navigation isn't just cosmetic. I want to get better at using my blog as a tool for thought, so tinkering with layout isn't just window dressing. It's (very slowly) experimenting with scaffolding for my brain. Little things can help!

Using Emacs Lisp to batch-demote HTML headings for my static site

| blogging, 11ty, emacs

Assumed audience: People who have lots of HTML files used as input for a static site generator, might need to do a batch operation on them, and are open to doing that with Emacs Lisp. Which might just be me, but who knows? =)

HTML defines a hierarchy of headings going from <h1> to <h6>, which comes in especially handy when people are navigating with a screenreader or converting web pages to Org Mode. I think search engines might use them to get a sense of the page's structure, too. On my blog, the hierarchy usually goes like this:

  • <h1>: site title,
  • <h2>: blog post titles, since I put multiple blog posts on the main page and category pages (ex: blogging)
  • <h3>: blog post's subheadings, if any
  • <h4>: I rarely need subsubheadings in my main blog posts, but they're there just in case

While fiddling with my blog's CSS so that I could try this fluid type scale, I realized that the subheadings in my exported blog entries started at <h2> instead of <h3>. This meant that the outline was this:

  • Site title
    • Blog post 1
    • Subheading 1
    • Subheading 2
    • Blog post 2
    • Subheading 1
    • Subheading 2
    • Blog post 3

I wanted the outline to be this:

  • Site title
    • Blog post 1
      • Subheading 1
      • Subheading 2
    • Blog post 2
      • Subheading 1
      • Subheading 2
    • Blog post 3

This was because I hadn't changed org-html-toplevel-hlevel during my 11ty export process. To solve this for new posts, I added a new option org-11ty-toplevel-hlevel that defaults to 3 in ox-11ty.el, re-exported one of my long blog posts to test it, and confirmed that my headings now started at <h3>.

I still had all my old HTML files with the wrong levels of headings. I wrote some Emacs Lisp to shift the headings downwards (h5 to h6, h4 to h5, h3 to h4, h2 to h3) in a file if it had an <h2> in it. Regular expressions are usually not a good idea when it comes to HTML because there might be exceptions, but I figured it was a pretty small and low-risk change, so I decided not to use the full XML/DOM parsing functions. I saved all the blog posts under version control just in case I messed things up. Here's my function:

(defun my-html-shift-headings (filename)
  "Shift heading tags in FILENAME."
  (interactive "FFile: ")
  (let ((case-fold-search t)) ; make the search case-insensitive
    (with-temp-buffer
      (insert-file-contents filename)
      (goto-char (point-min))
      ;; Only modify the files where we have an h2
      (when (or (search-forward "<h2" nil t)
                (search-forward "</h2>" nil t))
        (goto-char (point-min))
        ;; Handle both opening and closing tags
        (while (re-search-forward "<\\(/\\)?h\\([2-5]\\)\\>" nil t)
          (let* ((closing-tag (match-string 1))
                 (heading-level (string-to-number (match-string 2)))
                 (new-level (1+ heading-level)))
            (replace-match (concat "<" closing-tag "h" (number-to-string new-level)))))
        (write-file filename)
        filename))))

Running it on all the source HTML files in specific subdirectories was easy with directory-files-recursively.

(dolist (dir '("~/proj/static-blog/blog"
               "~/proj/static-blog/content"))
  (mapc 'my-html-shift-headings
        (directory-files-recursively
         dir
         "\\.html\\'")))

Then I could just rebuild my blog and get all the right heading levels. Spot-checks with Inspect Element show that the headings now have the right tags, and org-web-tools-read-url-as-org now picks up the right hierarchy for the page.

Correcting the input files was easier and more efficient than modifying my 11ty template engine to shift the heading levels whenever I build my site (probably by defining a preprocessor). I could've written a NodeJS script to do that kind of file manipulation, but writing it in Emacs Lisp matched how I might think of doing it interactively. Using Emacs Lisp was also easy to test on one or two files, check the list of files matched by directory-files-recursively, and then run it on everything.

Going forward, the new org-11ty-toplevel-hlevel variable should properly modify the behaviour of Org's HTML export to get the headings at the right level. We'll see!

Moving 18 years of comments out of Disqus and into my 11ty static site

| 11ty, blogging

Assumed audience: Technical bloggers who like:

  • static site generators: this post is about moving more things into my SSG
  • XML: check out the mention of xq, which offers a jq-like interface
  • or Org Mode: some notes here about Org Babel source blocks and graphing

I've been thinking of getting rid of the Disqus blog commenting system for a while. I used to use it in the hopes that it would handle spam filtering and the "someone has replied to your comment" notification for me. Getting rid of Disqus means one less thing that needs Javascript, one less thing that tracks people in ways we don't want, one less thing that shows ads and wants to sell our attention. Comments are rare enough these days, so I think I can handle e-mailing people when there are replies.

There are plenty of alternative commenting systems to choose from. Comentario and Isso are self-hosted, while Commento (USD 10/month) and Hyvor Talk (12 euro/month) are services. Utterances uses Github issues, which is probably not something I'll try as quite a few people in the Emacs community are philosophically opposed to Github. Along those lines, if I can find something that works without Javascript, that would be even better.

I could spend a few years trying to figure out which system I might like in terms of user interface, integration, and spam-filtering, but for now, I want to:

Fortunately, there's 11ty/eleventy-import-disqus (see zachleat's blog post: Import your Disqus Comments to Eleventy)

Exploring my disqus.xml with xq, Org Babel, and seaborn

One challenge: there are a lot of comments. How many? I got curious about analyzing the XML, and then of course I wanted to do that from Emacs. I used pipx install yq to install yq so that I could use the xq tool to query the XML, much like jq works.

My uncompressed Disqus XML export was 28MB. I spent some time deleting spam comments through the web interface, which helped with the filtering. I also deleted some more comments from the XML file as I noticed them. I needed to change /wp/ to /blog/, too.

This is how I analyzed the archive for non-deleted posts, uniquified based on message. I'll include the full Org source of that block (including the header lines) in my blog post so that you can see how I call it later.

#+NAME: analyze-disqus
#+begin_src shell :var rest="| length | \"\\(.) unique comments\"" :exports results
~/.local/bin/xq -r "[.disqus.post[] |
   select(.isDeleted != \"true\" and .message) |
   {key: .message, value: .}] |
  map(.value) |
  unique_by(.message) ${rest}" < disqus.xml
#+end_src

When I evaluate that with C-c C-c, I get:

8265 unique comments

I was curious about how it broke down by year. Because I named the source code block and used a variable to specify how to process the filtered results earlier, I can call that with a different value.

Here's the call in my Org Mode source:

#+CALL: analyze-disqus(rest="| map(.createdAt[0:4]) | group_by(.) | map([(.[0]), length]) | reverse | [\"Year\", \"Count\"], .[] | @csv") :results table output :wrap my_details Table of comment count by year
Table of comment count by year
Year Count
2025 26
2024 43
2023 34
2022 40
2021 55
2020 131
2019 107
2018 139
2017 186
2016 196
2015 593
2014 740
2013 960
2012 784
2011 924
2010 966
2009 1173
2008 1070
2007 98

I tried fiddling around with Org's #+PLOT keyword, but I couldn't figure out how to get the bar graph the way I wanted it to be. Someday, if I ever figure that out, I'll definitely save the Gnuplot setup as a snippet. For now, I visualized it using seaborn instead.

Code for graphing comments by year
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

df = pd.DataFrame(data[1:], columns=data[0])
df['Count'] = df['Count'].astype(int)
df['Year'] = df['Year'].astype(int)
df = df.sort_values('Year')
plt.figure(figsize=(12, 6))
ax = sns.barplot(x='Year', y='Count', data=df)
plt.title('Comments by Year (2007-2025)', fontsize=16, fontweight='bold')
plt.xlabel('Year')
plt.ylabel('Comments')
plt.xticks(rotation=45)
plt.grid(axis='y')
for i, v in enumerate(df['Count']):
    ax.text(i, v + 20, str(v), ha='center', fontsize=9)
plt.tight_layout()
plt.savefig('year_count_plot.svg')
return 'year_count_plot.svg'
year_count_plot.svg

Ooooooh, I can probably cross-reference this with the number of posts from my /blog/all/index.json file. I used Claude AI's help to come up with the code below, since merging data and plotting them nicely is still challenging for me. Now that I have the example, though, maybe I can do other graphs more easily. (This looks like a related tutorial on combining barplots and lineplots.)

Code for graphing
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import json
from matplotlib.ticker import FuncFormatter
from datetime import datetime

with open('/home/sacha/proj/static-blog/_site/blog/all/index.json', 'r') as f:
    posts_data = json.load(f)

# Process post data
posts_df = pd.DataFrame(posts_data)
posts_df['Year'] = pd.to_datetime(posts_df['date']).dt.year
post_counts = posts_df.groupby('Year').size().reset_index(name='post_count')

# Convert to DataFrame
comments_df = pd.DataFrame(comment_data[1:], columns=comment_data[0])
comments_df['Count'] = comments_df['Count'].astype(int)
comments_df['Year'] = comments_df['Year'].astype(int)

# Merge the two dataframes
merged_df = pd.merge(post_counts, comments_df, on='Year', how='outer').fillna(0)
merged_df = merged_df.sort_values('Year')

# Calculate comments per post ratio
merged_df['comments_per_post'] = merged_df['Count'] / merged_df['post_count']
merged_df['comments_per_post'] = merged_df['comments_per_post'].replace([np.inf, -np.inf], np.nan).fillna(0)

# Create a single figure instead of two subplots
fig, ax1 = plt.subplots(figsize=(15, 8))

# Custom colors
post_color = "#1f77b4"    # blue
comment_color = "#ff7f0e" # orange
ratio_color = "#2ca02c"   # green

# Setting up x-axis positions
x = np.arange(len(merged_df))
width = 0.35

# Bar charts on first y-axis
bars1 = ax1.bar(x - width/2, merged_df['post_count'], width, color=post_color, label='Posts')
bars2 = ax1.bar(x + width/2, merged_df['Count'], width, color=comment_color, label='Comments')
ax1.set_ylabel('Count (Posts & Comments)', fontsize=12)

# Add post count values above bars
for i, bar in enumerate(bars1):
    height = bar.get_height()
    if height > 0:
        ax1.text(bar.get_x() + bar.get_width()/2., height + 5,
                f'{int(height)}', ha='center', va='bottom', color=post_color, fontsize=9)

# Add comment count values above bars
for i, bar in enumerate(bars2):
    height = bar.get_height()
    if height > 20:  # Only show if there's enough space
        ax1.text(bar.get_x() + bar.get_width()/2., height + 5,
                f'{int(height)}', ha='center', va='bottom', color=comment_color, fontsize=9)

# Line graph on second y-axis
ax2 = ax1.twinx()
line = ax2.plot(x, merged_df['comments_per_post'], marker='o', color=ratio_color,
              linewidth=2, label='Comments per Post')
ax2.set_ylabel('Comments per Post', color=ratio_color, fontsize=12)
ax2.tick_params(axis='y', labelcolor=ratio_color)
ax2.set_ylim(bottom=0)

# Add ratio values near line points
for i, ratio in enumerate(merged_df['comments_per_post']):
    if ratio > 0:
        ax2.text(i, ratio + 0.2, f'{ratio:.1f}', ha='center', color=ratio_color, fontsize=9)

# Set x-axis labels
ax1.set_xticks(x)
ax1.set_xticklabels(merged_df['Year'], rotation=45)
ax1.set_title('Blog Posts, Comments, and Comments per Post by Year', fontsize=16, fontweight='bold')
ax1.grid(axis='y')

# Add combined legend
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

# Layout and save
plt.tight_layout()
plt.savefig('posts_comments_analysis.svg')
return 'posts_comments_analysis.svg'
posts_comments_analysis.svg

Timeline notes:

  • In this graph, comments are reported by the timestamp of the comment, not the date of the post.
  • In 2007 or so, I moved to Wordpress from planner-rss.el. I think I eventually imported those Wordpress comments into Disqus when I got annoyed with Wordpress comments (Akismet? notifications?).
  • In 2008 and 2009, I was working on enterprise social computing at IBM. I made a few presentations that were popular. Also, mentors and colleagues posted lots of comments.
  • In 2012, I started my 5-year experiment with semi-retirement.
  • In 2016, A+ was born, so I wrote much fewer posts.
  • In 2019/2020, I wrote a lot of blog posts documenting how I was running EmacsConf with Emacs, and other Emacs tweaks along the way. The code is probably very idiosyncratic (… unless you happen to know other conference organizers who like to do as much as possible within Emacs? Even then, there are lots of assumptions in the code), but maybe people picked up useful ideas anyway. =)

What were my top 20 most-commented posts?

Emacs Lisp code for most-commented posts
(let* ((json-object-type 'alist)
       (json-array-type 'list)
       (comments-json (json-read-file "~/proj/static-blog/_data/commentsCounts.json"))
       (posts-json (json-read-file "~/proj/static-blog/_site/blog/all/index.json"))
       (post-map (make-hash-table :test 'equal)))
  ;; map permalink to title
  (dolist (post posts-json)
    (let ((permalink (cdr (assoc 'permalink post)))
          (title (cdr (assoc 'title post))))
      (puthash permalink title post-map)))
  ;; Sort comments by count (descending)
  (mapcar
   (lambda (row)
     (list
      (cdr row)
            (org-link-make-string
       (concat "https://sachachua.com" (symbol-name (car row)))
       (with-temp-buffer
         (insert (or (gethash (symbol-name (car row)) post-map) (symbol-name (car row))))
         (mm-url-decode-entities)
         (buffer-string)))))
   (seq-take
    (sort comments-json
          (lambda (a b) (> (cdr a) (cdr b))))
    n)))
97 blog/contact
88 Even more awesome LotusScript mail merge for Lotus Notes + Microsoft Excel
75 blog/about
45 How to Learn Emacs: A Hand-drawn One-pager for Beginners / A visual tutorial
42 Planning an Emacs-based personal wiki – Org? Muse? Hmm…
38 Married!
37 Moving from testing to development
36 What can I help you learn? Looking for mentees
33 Lotus Notes mail merge from a Microsoft Excel spreadsheet
30 Nothing quite like Org for Emacs
30 Org-mode and habits
29 zomg, Evernote and Emacs
25 Literate programming and my Emacs configuration file
25 Reinvesting time and money into Emacs
23 The Gen Y Guide to Web 2.0 at Work
22 Drupal: Overriding Drupal autocompletion to pass more parameters
21 Rhetoric and the Manila Zoo; reflections on conversations and a request for insight
20 This is a test post from org2blog
19 Agendas
19 Paper, Tablet, and Tablet PC: Comparing tools for sketchnoting

Top 3 by year. Note that this goes by the timestamp of the post, not the comment, so even old posts are in here.

Emacs Lisp code for most-commented posts by year
(let* ((json-object-type 'alist)
       (json-array-type 'list)
       (comments-json (json-read-file "~/proj/static-blog/_data/commentsCounts.json"))
       (posts-json (json-read-file "~/proj/static-blog/_site/blog/all/index.json"))
       by-year)
  (setq posts-json
        (mapcar
         (lambda (post)
           (let ((comments (alist-get (intern (alist-get 'permalink post)) comments-json)))
             (if comments
                 (cons (cons 'comments (alist-get (intern (alist-get 'permalink post)) comments-json 0))
                       post)
               post)))
         posts-json))
  (setq by-year
        (seq-group-by
         (lambda (o)
           (format-time-string "%Y"
                               (date-to-time
                                (alist-get 'date o))
                               "America/Toronto"))
         (seq-filter (lambda (o) (alist-get 'comments o)) posts-json)))
  (org-list-to-org
   (cons 'unordered
         (seq-keep
          (lambda (year)
            (list
             (org-link-make-string (concat "https://sachachua.com/blog/" (car year))
                                   (car year))
             (cons 'unordered
                   (mapcar
                    (lambda (entry)
                      (list (format "%s (%d)"
                                    (org-link-make-string
                                     (concat "https://sachachua.com" (alist-get 'permalink entry))
                                     (with-temp-buffer
                                       (insert (alist-get 'title entry))
                                       (mm-url-decode-entities)
                                       (buffer-string)))
                                    (alist-get 'comments entry))))
                    (seq-take
                     (sort
                      (cdr year)
                      (lambda (a b) (> (alist-get 'comments a)
                                       (alist-get 'comments b))))
                     n)))))
          (nreverse by-year)))))

As you can probably tell, I love writing about Emacs, especially when people drop by in the comments to:

  • share that they'd just learned about some small thing I mentioned in passing and that it was really useful for this other part of their workflow that I totally wouldn't have guessed
  • point out a simpler package or built-in Emacs function that also does whatever clever hack I wrote about, just in a more polished way
  • link to a blog post or code snippet where they've borrowed the idea and added their own spin

I want to keep having those sorts of conversations.

Deleting spam comments via the Disqus web interface and Spookfox

8000+ comments are a lot to read, but it should be pretty straightforward to review the comments at least until 2016 or so, and then just clean out spam as I come across it after that. I used the Disqus web interface to delete spam comments since the isSpam attribute didn't seem to be reliable. The web interface pages through comments 25 items at a time and doesn't seem to let you select all of them, so I started tinkering around with using Spookfox to automate this. Spookfox lets me control Mozilla Firefox from Emacs Lisp.

(progn
  ;; select all
  (spookfox-eval-js-in-active-tab "document.querySelector('.mod-bar__check input').click()")
  (wait-for 1)
  ;; delete
  (spookfox-eval-js-in-active-tab "document.querySelectorAll('.mod-bar__button')[2].click()")
  (wait-for 2)
  ;; click OK, which should make the list refresh
  (spookfox-eval-js-in-active-tab "btn = document.querySelectorAll('.mod-bar__button')[1]; if (btn.textContent.match('OK')) btn.click();")
  (wait-for 4)
  ;; backup: (spookfox-eval-js-in-active-tab "window.location.href = 'https://sachac.disqus.com/admin/moderate/spam'")
  )

I got to the end of the spam comments after maybe 10 or 20 pages, though, so maybe Disqus had auto-deleted most of the spam comments.

It's almost amusing, paging through all these spammy attempts at link-building and product promotion. I didn't want to click on any of the links since there might be malware, so sometimes I used curl to check the site. Most of the old spam links I checked don't even have working domains any more. Anything that needed spam didn't really have lasting power. It was all very "My name is Ozymandias, king of kings: / Look on my works, ye Mighty, and despair!"… and then gone.

Modifying eleventy-import-disqus for my site

Back to eleventy-import-disqus. I followed the directions to make a contentMap.json and removed the trailing , from the last entry so that the JSON could be parsed.

Modifications to eleventy-import-disqus:

  • The original code created all the files in the same directory, so I changed it to create the same kind of nested structure I use (generally ./blog/yyyy/mm/post-slug/index.html and ./blog/yyyy/mm/post-slug/index.11tydata.json). I decided to store the Disqus comments in index.json, which is lower-priority than .11tydata.json. fs-extra made this easier by creating all the parent directories.
  • Ignored deleted messages
  • Discarded avatars
  • Did some reporting to help me review potential spam
  • Reparented messages if I deleted their parent posts
  • Indent the thread JSON nicely in case I want to add or remove comments by hand

With the thread JSON files, my blog takes 143 seconds to generate, versus 133 seconds without the comments. +10 seconds isn't too bad. I was worried that it would be longer, since I added 2,088 data JSON files to the build process, but I guess 11ty is pretty efficient.

Next steps

It had been nice to have a comment form that people could fill in from anywhere and which shared their comments without needing my (often delayed) intervention. I learned lots of things from what people shared. Sometimes people even had discussions with each other, which was extra cool. Still, I think it might be a good time to experiment with alternatives. Plain e-mail for now, I guess, maybe with a nudge asking people if I could share their comments. Mastodon, too - could be fun to make it easy to add a toot to the static comments from mastodon.el or from my Org Mode inbox. (Update 2025-03-30: Adding Mastodon toots as comments in my 11ty static blog) Might be good to figure out Webmentions, too. (But then other people have been dealing with spam Webmentions, of course.)

Comment counts can be useful social signals for interesting posts. I haven't added comment counts to the lists of blog posts yet. eleventy-import-disqus created a commentsCounts.json, which I could use in my templates. However, I might change the comments in the per-post .json file if I figure out how to include Mastodon comments, so I may need to update that file or recalculate it from the posts.

Many of the blogs I read have shifted away from commenting systems, and the ones who still have comments on seem to be bracing for AI-generated comment spam. I'm not sure I like the way the Internet is moving, but maybe in this little corner, we can still have conversations across time. Comments are such a wonderful part of learning out loud. I wonder how we can keep learning together.

View org source for this post

Old-school blogger

| blogging, writing

Text from sketch

Old-school blogger

[timeline showing different strands braided together]

I started blogging in 2001 (really, more like 2002), as a university student who had started playing around enjoyed learning out loud. Both blogging and Emacs continued through:

  • teaching computer science
  • going on a technical internship in Japan
  • taking up graduate studies
  • working at IBM
  • experimenting with consulting and semi-retirement
  • parenting

directly related to blogging: grad studies, working, experimenting

It's wonderful having such a long archive. I can trace my growth. I've changed a lot over the past 24 years. I miss being so optimistic and energetic, but who I am now and who I'm becoming are also okay.

[drawing of the butterfly life cycle]

  • caterpillar
  • chrysalis: We're in this messy stage where I digest myself and move my insides around
  • butterfly: maybe someday

Learning out loud by blogging:

  • Springboard: Writing as I learn means I can use my notes to pick up from where I left off.
  • Sometimes my notes help other people.
  • Sometimes people share what they've been learning.
  • Writing helps me gather my tribe.

Questions to explore:

  • What do I want to learn? How?
  • What's nearby?
  • What might be useful
    • to my future self
    • to others

Looking forward - I want to…

  • draw more. It's fun.
  • deepen my reflections.
  • learn more.
  • prepare so I can keep doing this.

How can I improve workflows for capturing/thinking/sharing/finding?

What can I do so I can keep learning and writing all my life? How can I get even better at it?

sach.ac/2025-03-16-01

Dave Winer's looking for old school bloggers (also this) so that nudged me to think about how and why I blog.

Still writing

From How to Take Smart Notes (Sönke Ahrens):

If you want to learn something for the long run, you have to write it down. If you want to really understand something, you have to translate it into your own words.

Writing and sharing are part of how I learn. Taking notes helps me learn things that are bigger than my working memory or my uninterrupted time segments. Sharing my notes helps me find them again later on, since I can search the Internet from my phone. Also, if I share my notes, sometimes I get to learn from other people too, and sometimes my notes help people figure out stuff and then they can build on that.

It makes sense to me to share these notes on a blog on my own domain, with a chronological view and an RSS feed that makes it easier for other people to check for updates if they want. Well, some other people. I suppose RSS readers are still a fairly technical sort of thing, and I don't particularly like posting on platforms like Facebook or LinkedIn. Anyway, I'll just keep writing here, and maybe people will come across posts via search engines or figure out how to get updates however they want to.

Summarizing posts
(let* ((annotations '((2001 "university")
                      (2003 "graduated, teaching")
                      (2004 "internship in Japan")
                      (2005 "grad school")
                      (2007 "working at IBM")
                      (2008 "drawing")
                      (2012 "experiment with semi-retirement")
                      (2016 "A+ was born")
                      (2019 "EmacsConf, COVID-19")
                      (2022 "SuperNote A5X")
                      (2023 "even more EmacsConf automation")
                      (2024 "cargo bike")
                      (2025 "added iPad to the mix")))
       (json-array-type 'list)
       (json-object-type 'alist)
       (posts-by-year
        (mapcar
         (lambda (o) (cons (car o) (length (cdr o))))
         (seq-group-by
          (lambda (o) (substring (alist-get 'date o) 0 4))
          (json-read-file "~/proj/static-blog/_site/blog/all/index.json")))))
  (append
   '(("Year" "Posts" "Note") hline)
   (cl-loop
    for i from 2001 to 2025
    collect
    (list (format "[[https://sachachua.com/blog/%d][%d]]"
          i i)
          (alist-get (number-to-string i) posts-by-year nil nil #'string=)
          (or (car (alist-get i annotations)) "")))))
Year Posts Note
2001 3 university
2002 31  
2003 869 graduated, teaching
2004 971 internship in Japan
2005 678 grad school
2006 877  
2007 510 working at IBM
2008 421 drawing
2009 452  
2010 399 Quantified Self
2011 397  
2012 361 experiment with semi-retirement
2013 359  
2014 339  
2015 251  
2016 141 A+ was born
2017 145  
2018 176  
2019 121 EmacsConf, COVID-19
2020 94  
2021 132  
2022 78 SuperNote A5X
2023 122 even more EmacsConf automation
2024 148 cargo bike
2025 49 added iPad to the mix

I don't see myself giving up these tools until I really can't use them any more. I'm keeping an eye out for assistive technology that might help me work around my limitations and the likely cognitive/physical decline I'll eventually run into. I'm encouraged by the fact that quite a few people manage to keep learning and writing even into their 80s and 90s.

Some weeks, Emacs News is all I can squeeze in: a long categorized list of links. When I have more time, I add little bits of code, drawings, reflections.

I love writing about little tweaks. Mostly that's about Emacs. I love the way I can shape it into something that fits me.

I like to summarize books and ideas as sketchnotes so that I have a chance of remembering what I want to learn from them. Also, the drawings are handy for sharing with others, and they're a way of giving back.

I'm slowly learning to write about life in a way that helps me learn more while respecting people's privacy. I like doing little experiments. Even tinier than the ones described in Tiny Experiments. Not "I will write 100 blog posts over the next 100 days," but rather, "What if I postpone fretting about A+'s homework until Saturday? What happens then?"

Writing workflow

After I get the kiddo through the morning routine and ready for virtual school, I usually play piano for about an hour or so. Then it's recess and some more hugs, and then I settle down for some writing or drawing. The weather is getting better, so I'm looking forward to moving some of that outside. Maybe I'll dust off those baby monitor apps so I can hear if A+ needs any help.

I mostly write on my laptop using Org Mode in Emacs. Org Mode is great for literate programming. I can mix my notes and my code however I like.

I don't write in a straightforward way. I jump around. I go on tangents and down rabbit-holes. It helps a little if I've sketched my thoughts beforehand, like for this post, or if I've done some audio braindumping to help me figure out where the interesting thoughts are. Sometimes I capture little thoughts on my phone and then move them to the post I'm working on. I'm trying to figure out how to chunk my thoughts better.

I have a lot of Emacs tweaks to make it easier to link to blog posts, bookmarks, sketches, sites from search results. I like including the text of sketches, too.

I use the 11ty static site generator to make my blog. I switched to it a few years ago because I didn't want to worry about keeping Wordpress secure. I don't have room for many programming languages in my brain at the moment, so I like the fact that 11ty uses JavaScript. It takes me about five minutes to compile my blog.

Reading workflow

From Dan Cullum: The more I read:

There is a strong correlation between the amount I’m reading, and the ideas I have for this blog. When I’m reading a lot, I feel like I have ideas coming out my eyes.

Reading makes me want to write, too.

I love the Toronto Public Library enough to transplant myself from the tropics and learn how to deal with winter. I've been reading more e-books lately. It's easier to highlight e-books compared to paper books. I can pick them up and put them down easily, and keep the pages open when I'm taking notes. I don't have to worry about misplacing them, either. I have some code to grab my highlights as a JSON, and then I can do things with them: include them in blog posts, add them to my personal notes, etc.

Not everything is available as an e-book, though, and sometimes the e-books have long hold times. Paper books are still handy enough.

I like reading blogs. They're much shorter than books are, and much less fluffy. Sometimes I feel like mainstream printed books have a lot of padding because of the considerations of the publishing industry: the book must be a certain size so it doesn't get lost on the bookstore shelf; the book must have a certain weight and thickness so people feel that it's worth $25. Blog posts can just get to the core of the idea instead of belabouring the point. I like the fractal density of hyperlinked text, too, and the conversational possibilities of it. It's a lot easier to bounce an idea back and forth to develop it when you can post in a day instead of waiting for a year for a book to be published.

I like reading on the new iPad. It's smaller than my laptop and bigger than my phone. It's easy to browse through blogs on it, unlike on my Supernote. I'm starting to develop a workflow for reading and writing smaller snippets: (toot)

  1. Read in NetNewsWire.
  2. Open interesting posts in Chrome on the iPad.
  3. Highlight the text.
  4. Use "Copy Link with Highlight".
  5. Tap on the selection again. Use "Share" to send it to Ice Cubes, a Mastodon client that can post to my GoToSocial instance and let me use my full post limit (5,000 characters, mwahahahaha).
  6. Paste the link into the toot, add my own thoughts, and post it.

I like linking to text fragments. Sharing from a webpage on my Android phone does this automatically. "Copy Highlight as Link" works from Chrome on the iPad. It saves people that little bit of scrolling or finding, although I suppose it would be helpful for people to go through the context before that selection. Alternatively, I could share directly from NetNewsWire and just link to the blog post instead of the text.

I like making visual book notes. They help me read a book well, and turning the sketch into a blog post gives me more opportunities to revisit it: when I write the post, and if someone comments or shares it.

Eventually I want to dust off my code for collecting Mastodon posts into a blog post, and maybe also re-establish a weekly review process.

Tangent: Check out Reading more blogs; Emacs Lisp: Listing blogs based on an OPML file for a table of the blogs I'm reading, along with the code I used to make a table of blogs, their latest post (as of the time I wrote my post, of course), and the post date.

Keeping an eye on the future

As the kiddo becomes more independent ("Mom, I'm 9, you don't have to fret about my jacket"), I'll have more time for myself. This is a good time to go bike and walk and explore outside, and to go deep and wide into our interests as a family. I do about 2-4 hours of consulting a week, just the stuff I'm interested in. (TODO: There's a tangent I want to write about interest-based nervous systems, which I notice in both A+ and myself, and probably building on this 2014 reflection on having a buffet of goals.) The rest is life time, divided among the things we want to learn/do/share and the things we do to take care of ourselves.

Even though I have increasing autonomy when it comes to time, and an increasing amount of focused time, I still haven't gotten to the bottom of my idea list or my to-write list. I don't think I'll ever get to the bottom of those lists, actually. I come up with ideas faster than I can do them. That's a good problem to have.

It makes sense to prepare for a couple of changes that will likely come up:

  • Age-related farsightedness: It'll probably get harder to read small text, and I might eventually need to juggle my regular glasses as well as reading glasses. (W- already does this occasionally. He prefers having different pairs of glasses instead of bifocals or progressives, and his reasons seem sound. I don't want to have to adopt different postures to see out of different zones of glasses.) Developing good workflows for reading will probably help here. Also, the cargo vests I wear will probably help me with the "Where are my glasses?" problem.
  • Menopause will probably rewire my brain a lot. I hear brain fog and tip-of-the-tongue can be challenging (see also Brain fog in menopause).
  • My mom is 79 and running into issues with cognitive and physical decline. She has a hard time typing, speaking, remembering, deciding, or feeling good. On the other hand, there are examples of people who have stayed sharp for decades. There are lots of factors that are beyond my control. Still, it would be nice to see if I can stack the deck a little. So yes to:
    • walks, bike rides, exercise, and maybe I can figure out a fun way to improve strength;
    • lots of learning and sharing and connecting
    • and experiments with technological and cognitive aids, like speech recognition to work around typing, text-to-speech interfaces to work around vision, notes to work around working memory, and maybe large language models to work around issues with recall.
    • … and I might as well learn Morse code or explore accessibility tools, just in case I'm limited to twitching cheek muscles or something like that.

The life expectancy at birth for the Philippines for women born in 1983 is ~65 years; in Canada, about ~80 years. I want to keep learning and writing and sharing for as many of those years as I can.

See discussion on Mastodon

View org source for this post