Freewheeling Apps are a durable substrate for the future of software.

Easy to download. Easy to run. Easy to modify. Easy to share.

  • Free and open source. I'm not trying to make money from them, only meaning. I'm beholden to nobody. No dark patterns, ever. I'll never sell them to someone else; there's nothing to sell.
  • Built atop a small, secure and trustworthy platform I don't control. The download is 5MB. No install process on most OSs.
  • All apps contain their source code; you can always look inside the app you're running. You can always modify it using the same software you use to run it. Running your modifications takes a press of a button. Freewheeling apps reward curiosity.
  • Each app quickly stabilizes, and then seldom updates. Because timeless software is safer and more trustworthy (it can't change its mind). Definitely no auto-updates. I think I might be able to get to 50 years without an update. Join me on that voyage!
  • Individual apps stay simple; apps spawn forks and remixes rather than grow complex.

More details in this talk & transcript.

To start using your computer more freely and safely, try out one of these apps (the others can wait):

Coming soon: networking. Freewheeling Apps will become much more useful when you can use them to get your data out of other silos service providers.

Subscribe to the RSS feed (?)
Subscribe to the html journal (?)
Back to pre-LÖVE (2022)
Back to Mu epoch (2018-2021)
Devlog
Nov 7, 2024
I have gotten annoyed by timezone calculations for hopefully the final time. Here's a static html page you can download and save locally to roughly compare times in different timezones (just hours; you're on your own for minutes).

The way I'm naïvely imagining using this:

  • Scroll to the timezone you know the time in.
  • Click on the nearest hour.
  • It'll highlight that column all over the page.
  • Scroll or find the timezone you care about.
    • If you're not in a whole-number timezone (Hello India), you'll need to do some additional mental arithmetic by comparing nearby rows.

That's it. Since it's almost entirely static, you can always be sure that you're seeing the same thing on this page as anyone else.

Unfortunately you need to know if you're in daylight savings time or not, something that is often beyond me. I'm not sure what to do about that without reintroducing dynamism that takes the current computer's time into account. Then I again end up wondering if others are seeing what I'm seeing.

There are a few abbreviations for America, Europe and Australia in both pages. You can now see *ST and *DT on either page, which might help if you're not observing daylight time yet, but someone else is. There's a tension here between trying not to be overwhelming and emphasizing the Western or Northern hemisphere. My thinking is to only add codes for longitudes with lots of cities or with daylight savings time. Hopefully people in Bhutan or Nigeria or the Chatham Islands won't hold it against me.

Inspired by Bret Victor, but of course the inevitable mistakes are all mine.

permalink

* *
Oct 26, 2024
I've been getting back into teaching kids programming 1:1. Of course, this time using Lua, LÖVE and Carousel. After a couple of months, it occurred to me to collect all my little impromptu puzzles and exercises into a single app anyone can go through on their own schedule.

Carousel Cards (LÖVE app, really just a zip file containing source code, 169KB)


    Lua Carousel showing 6 lines of text:

    What does this program do?
    If you don't know, guess!
    Then hit the 'run' button and check your answer.
    There are no penalties for answering wrong.

    print(3+4)

Nowhere near done yet. But it has 50 112 little "levels", each taking between a few seconds and a minute. A full game/curriculum might need 2000 levels or something.

Inspirations:

permalink

* *
Oct 21, 2024
Emacs-style ranges on a text buffer that I can now hang attributes like color, decorations and click handlers on to.

  • Inserting/deleting text before a range moves it.
  • Inserting/deleting text after a range leaves it unchanged.
  • Inserting/deleting text within a range grows/shrinks it.
  • Deleting text at a boundary shrinks the range, and deletes it when it becomes empty.
  • Inserting text at boundaries can't disambiguate whether I want the text within or outside the boundaries. But I can grab some handles on the range to adjust.

The final complexity cost was 200 lines but it was a non-linear path getting there. I started out with the notion of pivots from my doodle app. There, pivots live inside the data structure for a single line of text. Since ranges now need two pivots that could be on different lines, I had to move them out. I started tracking pivots in a separate data structure, maintaining bidirectional mappings between pivots and their locations, and then tracking ranges as combinations of pivots. This quickly blew up the implementation to 500 lines, and I was juggling 27 manual tests of which half were failing.

The next day I started from scratch and threw out the notion of pivots entirely. Now I just maintain 2 locations directly inside each range, and linearly scan through them all for any book-keeping. The implementation dropped to 200 lines and the tests passed fairly quickly.

Earlier this year I threw out an implementation after suffering with it for 2+ years. It feels like I'm getting the hang of this programming thing that I threw out an implementation now after just 2 days. I'm setting a higher bar for elegance. At the same time, it's interesting that my instinct remains as poor as ever for the right approach in even a slightly new problem. Here I spent a lot of time trying to squeeze my ranges into lines so that deleting a line would transparently delete ranges within it. But I blindly assumed a fully normalized design with a first-class notion of a pivot must be a good idea.

As I linked to in the previous post, this app was inspired by this comment by Alex Kladov about how Emacs maps ranges of text to attributes.

permalink

* *
Oct 18, 2024
I've been thinking about Potluck and the Emacs model of text augmented with attributes for ranges. Potluck describes 3 mechanisms:

  1. extracting data using (regex) patterns
  2. performing computations on that data
  3. showing results of computation as annotations

My notebook app does simple variants of 2 and 3, and replaces 1 with explicit in-document markup.

Now I'm playing with another approach to 1. I already have the idea of pivots from my doodle app. Putting two of those pivots together should yield a range that adjusts in intuitive ways in the presence of edits. An example may be a WYSIWYG UI for adding a hyperlink to some text:

  • Inserting/deleting text before a range moves it.
  • Inserting/deleting text after a range leaves it unchanged.
  • Inserting/deleting text within a range grows/shrinks it.
  • Deleting text at a boundary shrinks the range, and only deletes the attached attributes if the range becomes empty. This makes ranges more robust to deletion than my doodles which attached to a single pivot.
  • Inserting text at boundaries can't always do what you want. I imagine it'd be nice to have handles that you can drag to adjust a range.

permalink

* *
Oct 10, 2024
I hadn't tried this until today, but it turns out to work: I can create equations forwards and back in notebook.love, and trigger either selectively based on what blanks I fill in.

The fine print: to switch directions I have to fill in the right blank, clear the old blank, and then type in something outside the old blank (to indicate I'm not going to type further into the old blank).

Works better if I clear the old query first, but who can remember that?

I've decided to just recompute on every keypress and mouse click. It seemed unnecessary, but now I see that there's some benefit from the inefficiency.

permalink

* *
Oct 7, 2024
A text editor that supports doodling.

Ways I've previously seen text coexist with a canvas:

I'm instead using the game engine idea of a pivot. Any time I toggle into doodle mode I have to first pick a pivot from one of the characters on screen. All my drawings are relative to that pivot, and edits to text maintain pivots alongside.

Displacements to the pivot are preserved in font-derived units, so it looks "reasonable" as you resize the font.

Deleting a character deletes all drawings pivoted on it. (But there's undo.)

This took 200 lines, so not too much though it was more than I'd initially expected.

Inspired by this thread.

permalink

* *
Oct 6, 2024
Indicating how the notebook fills blanks

Blanks can be filled in either with the results of computation or with what a person typed in. Now we indicate such conflicts in two ways.

  • Blanks filled in from computations get a distinct look (the cyan background), separate from both hand-written (black on white) and computed, non-editable text (cyan on white)
  • If I manually edit a blank, its background changes, and any code that was overridden doesn't execute. Here are a couple of examples:


But perhaps the color choice is too strong:

permalink

* *
Oct 4, 2024
A notebook with tabs that live-update as you edit.

The sort of interaction I live for: someone wished something existed, and it gave me a great deal of pleasure to manifest it.

permalink

* *
Oct 2, 2024
This debug UI has been surprisingly handy over the last few days. I'm able to visualize a parse tree even after it's been flattened, just using color transparency. All I'm doing is painting all the rects that contain the mouse cursor. Since the color is transparent, it doesn't matter what order they're in. (The larger rects actually come later; they're fall-backs if a more fine-grained rect isn't found.)

permalink

* *
Sep 30, 2024
My notebook now synchronizes the results of computation into named blanks in prose.

Repo

permalink

* *
archive
projects
writings
videos
subscribe
Mastodon
RSS (?)
twtxt (?)
Station (?)