Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
1

2025-03-08

from Freewheeling Apps [alt+shift+b] in technology

It started, as it often does, with my kids. They got curious about a chess variant called Monster chess. But none of the websites out there have an implementation of it. So I set out to build one. In Lua Carousel, of course, my cross-platform app for editing and running little Lua scripts on phone, tablet or computer. Now, like all the best chess variants, this one uses all the rules of chess, just with one crucial, minimal tweak. So to implement Monster chess I had to first implement chess. Which I did (and not for the first time). But by the time I'd implemented all the legal moves, castling, en passant, pawn promotion, forbidden moving into check -- I was at 500+ lines of code. At that scale of program, Carousel's janky scrollbars get too thin for my fat fingers to acquire on the phone. Carousel's only nice up to 100-150 lines. So I switched to my template repository for standalone apps using the Carousel UI. I've done this before, most notably with my Sokoban client which you can edit the source code for right on your phone. Indeed, editing the source code is the expected way to skip 10 or 60 levels. On some level everything I do stems from the desire to believe that editing source code can be a nice user experience. I soon had a second chessboard app. But it felt like spaghetti. The Carousel template lets me split up a large program into multiple files, but with all the problems of text-based program navigation, and all my bespoke janky tools to boot. You're looking for a specific function. Which file is it in? It's hard to read code you didn't write, and on some level everything I do is an attempt to make it easier. You know, literacy. Computational literacy. The ability to read a novel of code or write an email of code. But we're not at the promised land yet, and meanwhile I have spaghetti nobody should bother with. So I ripped out all my Carousel stuff and made it just a plain LÖVE app. File size dropped from 128KB to 8KB. So clean! But now it's not...
9th Mar 2025

Stay updated

Get a weekly newsletter with the top 5 articles worth reading every week.

More from Freewheeling Apps

2026-05-24

What is text? This blog post reminds me of the time 10 years ago when I tried to do away with Linux's desktop environment because it seemed overly complex and hard to wrap my head around. I had a lot of trouble running wifi without the desktop environment, and it left me quite bitter. Linux had seemed like a hacker's OS when I first started out. But it was trying so hard to compete with Windows that it was starting to seem like Windows in all the wrong ways, it seemed. That experience contributed to me walking off into the wilderness to build Mu. I've gone back and forth between two tendencies in my life. On the one hand, I used to brag about a minimalist setup living in text mode, only running startx as needed, long after the world had moved to fully graphical desktop environments. On the other hand, I was frustrated early on with the limitation of pure text, creating a little bit of Vimscript to view and edit drawings inside a code fold inside code comments. (I don't remember what format I used for the drawings. Maybe Inkscape? I always liked its format and found it quite easy to hack manually.) And these poles have continued to attract me in turn. I wanted to build Mu with just text mode for a long time, and now in the past couple of years I seem to have tried to minimize my use of terminals, with all their ugly historical and path-dependent hacks, and to do more on a graphical canvas (of a game engine). Trying to look back dispassionately, I seem to operate by: Structured data. Not too much. Mostly text. But then, last year, I had a fascinating and also slightly frustrating conversation with a friend where we realized we had very different associations for the term "text". When I said I wanted to keep something as "just text" I meant "viewable with cat on a vt100." (But with Unicode.) When they said they wanted text they meant, "everything you could do in the tradition of book publishing before the advent of computers." (But maybe not images? Equations and line drawings, certainly. And video is right out.) Another dimension of our disagreement: I was thinking of text in terms of compatibility. What can you do regardless of what device you have on you right now. They were thinking in terms of "what's possible with an extremely low-power reader without regard to compatibility." Also, I imagined authoring text and reading it as happening on the same device. Perhaps they imagined authoring on high-powered devices and reading on low-powered ones. So, 30 years after I started programming, I still don't really understand what I want. I try to clear my mind of the cobwebs created by the hacky, historically path-dependent past. But I'm not there yet.

24th May 2026 • 1 votes
2026-01-28

As I start teaching the kids programming, I’ve been thinking about how to motivate it to them. They do a lot of drawing, so that seemed like a potential hook to show how drawing on the computer may make some things easier (erasing, manipulating, repeating) and other things harder (e.g. drawing well). Demo of a little bare-bones fork of Lua Carousel for a computer with a large screen and a mouse. The large screen is divided up between a code editor and a drawing area. There are a few drawing tools. When you make a stroke the code for it is inserted above the cursor in the code editor. You can also edit the code directly and run it to make changes to the drawing area. There is no support for grabbing points or shapes in the drawing area. All you can do there is emit shapes with coordinates. Everything else you gotta program the old-fashioned way. Download (120KB). Inspired by Ronin and Sketch-n-Sketch.

29th Jan 2026 • 1 votes
2025-10-30

An experiment involving scrollbars Keeping scrollbars easy to acquire even in large files. Demo of an idea for a scrollbar with a shadow or "penumbra" which makes it easier to acquire to drag around, even as the conventional and smaller central object continues to be visible to give an accurate sense of file size and our position in it. Thanks Artyom Bologov and Mary, though I ended up taking their suggestion in a different direction…

31st Oct 2025 • 1 votes
2025-09-02

As an experiment, I've machine-translated the help docs in Lua Carousel into 7 languages: Spanish (Peninsular and Latin American) German French Japanese Chinese Portuguese Hopefully it is helpful for someone to no longer have to put up with English. I put each language in a separate download to reduce the amount of data you need to download. Other languages are machine-translated from the English version. I've had friends spot-check it for obvious issues, but there are likely to be problems I missed. Please report any issues you spot. If you want to manually translate pages to improve wording and tone for your culture, contributions would be most welcome. No support yet for right-to-left languages, or languages with combining characters. Lua Carousel still assumes every character on screen is atomic. For example, I can support Chinese and Japanese but not Korean, Arabic or Indian languages. I don't know what I'm doing when it comes to multilingual keyboard support. My rough sense is that that's something to configure for your device. But if there's something I can change in Lua Carousel to make your life easier, please let me know. I've included different fonts for Chinese and Japanese, which blow up the download size for these languages. The source code inside each zip file download is exactly the same, only the font files are different. The larger font files also seem to take up to 10 seconds to load.

2nd Sep 2025 • 1 votes
2025-06-01

I've been reading The Nature of Order by Christopher Alexander, and it's inspired me to experiment with this representation to communicate the global structure of the source code of the Game of Snake: The whole program is arranged on a 2D surface within a series of nested boxes. You can hit ‘run’, and it extracts the code from the surface and runs it and you get to play a game of Snake. (Only on a computer; even though the app runs on a phone, you can't play this particular game of Snake without arrow keys.) So it can be viewed as a sort of Literate Program, even though the representation I'm tangling from has a richer UI than is typical, a surface you can pan and zoom around with a mouse wheel or touchscreen. (It doesn't support editing yet. This is a purely reading experience for now.) I wasn't trying to build yet another Literate Programming experience, though. What was on my mind was Christopher Alexander. Christopher Alexander (CA) was an architect, and he indirectly inspired Design Patterns in the software world. His magnum opus was The Nature of Order, (NoO) a book I'm not sure I'll ever finish. Not because it's long (it's true that it's 4 volumes, but it has a lot of pictures) but because I often need to put it down to think about the chapter I just read for a day. And then the day turns into a week or a month or a year. The first thing to know about The Nature of Order is it defeats attempts at summarization. With that out of the way, here's a summary of the fragments I've read: People build tools, and then the tools change people. In unanticipated ways, for better and for worse. Tools and buildings (remember, CA is an architect) that contribute to more functional societies have certain objective properties. The properties are hard to state or quantify, but have an objective reality analogous to the fundamental forces in Physics. In his earlier books, CA called this property "the quality without a name." In NoO, he calls this property "wholeness" or "life". The degree of life of an arrangement of matter is visible in its geometric form. Function matters, but CA rejects the cartesian separation between form and function. If something has good consequences, it's going to also look good. This sense of "looking good" is not subjective. People often have greater or lesser sensitivity to it, but we all inherently have the capacity to see that one arrangement has more life than another. The degree of life in an arrangement is a holistic property. You don't compute the life of all the parts and add them up. One poor property can destroy the life of the whole. As a best guess, life stems from approximately 15 properties. These properties establish a field of centers that integrate object with environment, foreground with background, inside with outside, form with function, matter with emptiness. Centers are made of centers, recursively, ad infinitum. Any attempt to create life by assembling homogeneous centers is doomed to fail. Instead, nature and the universe creates wholeness and life by starting with a seed and successively unfolding it, differentiating parts of it into centers, each step of the process intensifying existing centers, bringing new centers into being, and increasing the life of the whole. I'm not sure I buy it to the hilt, but it feels actionable even so. I don't need to believe in the objective reality of the vector field of centers to consider the possibility that it might point us in the direction of better quality as perceived by a lot of people. The image above is a very preliminary step at creating a more wholesome arrangement of the source code for the game of Snake, though CA would likely find it sophomoric. I'm trying to use some well-known ideas in software design. There's a top-down decomposition here. The game of Snake consists of two screens, one where you play the game and one when the player dies. The screen for the game itself (where the player spends the bulk of time) decomposes into 3 concepts: space, snake and food. At this point, however, things get muddy from a classic CS perspective. These concepts are not completely separate. There are places in the program where they coil into each other. For example, I can't entirely explain how a snake grows without reference to food, and I can't explain food without reference to its purpose: growing the snake. In response, I'm duplicating this code in multiple places on the surface, with slightly different emphasis: To me this evokes CA's property of interlock/ambiguity. The centers of space, snake and food don't have clear boundaries here, the boundary is ambiguous and the centers kinda intertwine. Another case of interlock/ambiguity: the screens for game and game over that I mentioned earlier are not completely separate. When the game ends, there's a great human need for some sort of conclusion to be derived from it, often in the form of a score. Did I do well or poorly? Set a personal best? Defeat my friend's score? Here the score takes into account the number of times the snake turned. You can imagine alternatives, but no matter how you calculate the score, it must make reference to the game that was just played. The two screens must share data. Here I am experimenting with indicating this permeable boundary by creating a gap in multiple levels of centers, close to uses of Nsteps, to show that the centers aren't crisply isolated from their surroundings. I spent some time on other touches. Rather than arrange code in a vertical rectangle as we usually do, I chose to break it up horizontally to make the boundaries between definitions more easily apparent. Viewed charitably, this might be seen as an example of what CA calls "alternating repetition", an attempt at a rhythm that resonates with the reader. Interspersing prose with code is another repetition, as are the columns. However, I tried to not make the columns or interspersed prose too monotonously regular. I tried to keep it "rough", and to hand-craft the spacing at each point rather than try to enforce some global policies for spacing and positioning. If you'd like to read a little more about Christopher Alexander and The Nature of Order, here are a couple more summaries: Beautiful Software is perhaps the most comprehensive archive of CA lore. In particular, see this early article about the fifteen properties. Dorian Taylor's obituary. A worked out example of the fifteen properties. Beware, you may get sucked into the full 4-volume opus.

1st Jun 2025 • 1 votes

More in technology

List of Vim forks

Here are the Vim forks I know about: Neovim: “a Vim-based text editor engineered for extensibility and usability, to encourage new applications and contributions.” Vim Classic: “a stable, long-term support fork of the ubiquitous text editor, maintained without the assistance of generative AI tools.” EVi: “a hard-fork of Vim v9.1.0 (Jan 2024) before AI was used in the project.” MacVim: “a macOS version of the Vim text editor, providing a graphical user interface for Vim, while tightly integrating with macOS and providing features specific to the platform.” This list doesn’t include Vim-inspired editors like Kakoune, Vim-style plugins for other editors like IdeaVim, or Vim “distributions” like LazyVim. Contact me if you know of other forks!

5 hours ago • 1 votes
iOS 27 has some weird Dynamic Island behavior

Thought I’d make a quick blog post about some weird behavior I’m seeing on iOS 27 and Dynamic Island live activities, that I initially thought was restricted to the Duo, but turns out affects everything. Yay. Maybe this post can save some others some time, and hopefully Apple can fix it. Long story short, ever since iOS 16.1 introduced “Live Activities”, where we can display handy little widgets in the Dynamic Island, there’s been a bunch of different configurations for showing things up there: something on the left, the right, when expanded, when two apps are showing things and yours gets a bit smaller, and on the lock screen. One interesting one is if you only need to show a small indicator in the Dynamic Island (screen recording indicator, electric vehicle charging status, etc.) you can do so, which just shows on the left on older iPhones, and on the Duo a single one below the camera cutout. Apple describes how to do this in their developer documentation (emphasis added): If you can’t find a solution to display views for leading and trailing compact presentations with limited width, provide a compact leading presentation that best represents your Live Activity, such as the view you use for the minimal presentation, and an empty view for the trailing compact presentation. The system hides the empty trailing view automatically. Unfortunately, in iOS 27 (and 27.1, and 27.2), this only half works, due to a new addition in iOS 27: isDynamicIslandLimitedInWidth. It’s a variable that basically tells you if the Dynamic Island is even narrower than normal, presumably because they knew the Duo was coming with its vertical Dynamic Island, but also because as of iOS 27, Live Activities now show in the Dynamic Island when the device is in landscape too. So it’s handy to know if you have a bit less space than usual. In this new narrow view however, it seems like they didn’t test all the configurations thoroughly, as if you provide EmptyView for that trailing compact presentation, it’s not empty at all, and in fact enforces a quite large minimum size so the Dynamic Island looks hilariously tall. In other words: The system hides the empty trailing view automatically Left: what it should look like. Right: reality You can imagine this is especially rough on the Duo, where navigation buttons, toolbar buttons, tab bar buttons, etc. are all competing for precious space on the right. But that’s only half true. It displays perfectly fine (hiding it) when isDynamicIslandLimitedInWidth is false (AKA how it always used to be), but in the new cases where it can be true, like the Duo and iPhones in landscape, that’s when it breaks. So tl;dr: In iOS 27, Dynamic Island Live Activities are improperly sized if you use the listed EmptyView configuration and isDynamicIslandLimitedInWidth is true. This makes it especially bad on the Duo, womp womp. Complications Now, you might be saying “That’s weird, I haven’t seen that behavior! This is nothing but hogwash!”. But it is not hogwash, just a little complicated. There’s basically three categories of apps. Apple apps Maps, for instance, displays this single, small Dynamic Island live activity perfectly well and sized, and has been beautifully compiled with the iOS 27.2 SDK. By all accounts it should show this bug, too! Apple apps don’t have to use the same APIs as us normies, so funnily enough through really cool tools like REA we can disassemble the iOS Simulator and see that it’s in fact true, the Apple Maps live activity just completely bypasses WidgetKit and instead just renders plain UIKit through MapsBannerViewController which shows the cute little cyan widget through a ManeuverAccessoryView and then for the hidden trailing view simply returns a DistanceAccessoryView with CGSizeZero, which iOS actually respects, unlike our paltry SwiftUI EmptyView. Third-party apps NOT updated to iOS 27 If your app hasn’t been compiled with any of the iOS 27 SDKs, it only knows about iOS 26, and thus doesn’t know about (or have to worry about) the new, troublesome isDynamicIslandLimitedInWidth property, so it just… displays perfectly well. Here’s the Rivian app, which hasn’t been updated for iOS 27 yet, which shows the Live Activity displaying perfectly sized on the Duo (indicating vehicle charging): (Technical disassembly detail for the nerds: iOS shows the app’s minimal config, a smaller config normally used when multiple apps are in the Dynamic Island together, rather than the normal compact config, so it bypasses this issue entirely.) Developers who updated their apps for iOS 27 Surprise! That picture from before? On the left is just me compiling with the old 26 SDK that works! Unfortunately if you are a good developer who updated their app for iOS 27 and all the new features, you kind of get punished through this bug, because your live activity displays the improperly sized way described above, and you can’t just use raw UIKit like Apple. So you’re kinda stuck where I am: do I ship a weirdly sized Dynamic Island experience for everyone? Do I shove something extraneous in the compactTrailing spot just so it doesn’t look as empty? Do I not update the app at all until Apple fixes this bug? Widgets are techincally a separate extension from your main app, so you could compile them with a different SDK (26) than your main app (27) which fixes this bug, but I’m not sure Apple approves of that. Apple On the bright side hopefully it’s an easy enough fix, treating EmptyView like you used to on iOS 26 and per the documentation (the part that is weirdly right beside the mention of the new API that introduces this craziness in the first place). Radar/feedback assistant ID: FB25301955 Easy reproduction code if you’re curious: import ActivityKit import SwiftUI import WidgetKit struct CircleAttributes: ActivityAttributes { struct ContentState: Codable, Hashable {} } struct BlueCircle: View { var body: some View { Circle() .fill(.blue) .frame(width: 20, height: 20) } } @main struct CircleLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: CircleAttributes.self) { _ in BlueCircle() } dynamicIsland: { _ in DynamicIsland { DynamicIslandExpandedRegion(.center) { BlueCircle() } } compactLeading: { BlueCircle() } compactTrailing: { EmptyView() // pls respect me } minimal: { BlueCircle() } } } }

7 hours ago • 1 votes
Going Somewhere

Vicky, driving me home from O'Hare, got sixteen dollars for the best conversation I had in months. The last line is hers.

3 days ago • 1 votes
An AI usage statement for bryanbraun.com

I’ve had “AI usage statements” in the back of my mind ever since I read Ethan Marcott’s blog post sharing a round-up of his favorite ones. I didn’t consider writing one for this site, since it had the smell of status-signaling. Like a quasi-political declaration of allegiance to some sort of in-group (be it pro-AI, anti-AI, etc). I couldn’t be less interested in that.1 But then something happened that caught me completely off-guard. I was reading a post on my friend’s personal blog. This was a guy that I knew personally and looked up to. I’ve been reading everything he had been writing for years. About two-thirds of the way through a long (and fairly insightful) blog post, I had a sudden realization that the post had been written by AI. I’m not the best at detecting this kind of thing, but once I saw it, I couldn’t unsee it. It was unmistakable. And the thing is, I didn’t feel indifferent or even disappointed, as I might have predicted. I felt betrayed. For somebody who considers themselves open-minded—even optimistic—towards the development of AI, I didn’t know where this feeling was coming from. I have theories… maybe it has something to do with the social contract between the writer and the reader. Whatever it is, I didn’t want anyone who was reading my blog (hi mom 👋) to feel the same thing. So for the sake of transparency, here is my AI usage statement: The content of this website is written by me. I do not use AI to generate the text you are reading because writing from scratch helps me process my thoughts (which is one of the main reasons I blog in the first place). I don’t currently use AI-powered writing tools (grammar-check, spell-check, dictation tools, etc), but I may experiment with them as long as they don’t undermine the thinking process. The code for this website was originally hand-written, but I have since used AI to make several useful updates. I expect to continue using AI to write code for this website and other personal coding projects. When I create a public project exclusively with AI, I mention that fact in the project README.2 I am experimenting and my AI usage may change. When it does, I’ll update the policy here on my /about page, which is where it will live long-term. To explain why, see Paul Graham's Keep your Identity Small. ↩ For an example, see s-tier ↩ Thanks for reading this post via RSS! Let me know your thoughts by leaving a comment on the original post or sending me an email.

4 days ago • 1 votes
Amend 4.0 Released

Amend 4.0 has been released: amend-4.0.sit (StuffIt 3 archive, includes source code and THINK C 5 project file) c838ee34931c00c03869affe7f9d6b85c0bbc51dd1242d11e8682a3774fdd0e3 SHA1: 040059d0b3302f1bd6ec0207b9a2f12dccdda374 Changes in this version: New repository format that tracks author e-mail in addition to name; existing repositories are migrated automatically on open Add inregrated resource file support; .rsrc files can be added directly to a repo and files are converted to DeRez-style ASCII output before diffing, so running SADerez to produce separate .r files on every change is no longer required Allow Command+. to cancel loading a repo Allow Command+. to cancel diffing; if diff text has already been output it will remain available, otherwise the window will be closed Center settings dialog, make sure author name is present Use database transactions to avoid leaving repository database files in a partially-committed state if we crash Numerous bug fixes

5 days ago • 1 votes
📚 BoredReading

You seem to be enjoying this.

Join free to unlock everything.

Create free account

Already have an account? Sign in