Latest Posts (20 found)
Unsung Today

Writing about fonts

In last week’s post , I made an off-hand comment about Vercel’s Geist Pixel announcement , and I thought it might be interesting to turn this into more of a full-fledged critique. I don’t think it’s a good announcement, but its flaws are pretty universal, so I want to put words to these flaws. This will extend to a lot of other writing about design, not even necessary even just about typography. Here’s my advice that I believe would make announcements like this better: I know the elephant in the room here is “how big companies do things.” A lot of redesign announcements and font unveils exist chiefly to make the execs who started it happy, and perhaps as fodder for future promotion – I bet the whole “Already shaping what’s next” section isn’t really written for external audience – and they get chewed by the big PR machine that often files away whatever personality and quirkiness might have been there. Your job is to fight the machine! But I acknowledge that it might be hard. However, I’ve also seen all this seeping into personal font announcements, which is unfortunate. (I don’t want to link to specific examples, since that’d be punching down.) Also, this is not just about the joy of reading or some general notion of “craft” – although they are important, too. This is also purely informational. I feel I haven’t learned enough from the Geist Pixel announcement for the amount of time I spent with it. I don’t understand “multiple variants for different densities and use cases” or “semi-mono approach” or what stylistic sets are included. (My general goal is to write in a way that people can learn something new from any design announcement, even if they don’t have any prior context, and if they never actually use the font.) It‘s a shame, because the work itself seems thoughtful and excellent, deserves a better intro, and could help others interested in typography as a jumping off point, particularly because this feels like a typeface off the beaten path. Just to round up this post, some recent counterexamples: #craft #david jonathan ross #storytelling #typography Write like a human being would. This is famously hard, and takes practice. Here, we see stuff like “unapologetically digital,” “a functional tool within a broader typographical system,” “the result feels both nostalgic and contemporary,” and “constraints weren’t a limitation, they were the design tool.” No one talks like this. I think people believe font releases have to use these words and phrases, as a way to bring legitimacy to the project. I do not subscribe to that way of thinking. I think it leads to writing that’s optimized only for admiration, which is not as much fun for anyone. Show a specific example of a problem you solved. This page hints at some things – “They don’t scale properly across viewports, their metrics conflict with existing typography, or they’re purely decorative.” – but that feels altogether too vague to be useful or even interesting. These are actually fascinating and hard challenges, yet I know as much at the bottom of the page as I did at the top. Show details you are proud of. Zoom in literally or figuratively. “Each glyph was manually refined to avoid visual noise, uneven weight distribution, and awkward diagonals.” I would love to see a few examples. Show work in progress! Show stuff you discarded. This will be hard, but why not? It’s good practice and I believe this, more than anything else, will have people appreciate what you did. Plus, everybody loves a blooper reel. Related: talk about struggle. Don’t motion in the direction of struggle, or performatively announce that this was the hardest project of your life. Actually talk about something that was hard, and why. Be vulnerable. Be honest. People didn’t care that Rocky lost in the first movie, because people cared about Rocky. Talk about your inspiration or history. What we all do here is part of something bigger. Why a pixel font to begin with? Why is this interesting to you? Is that because Vercel is filled with nerds, or because you got bored with bold and italic, or because it just seems visually interesting in a new way? Let me type! Immediately and everywhere. I don’t think any modern font announcement/​tester can exist without this. This is the easiest way to getting to know the font and explore specific things that matter to you. (To do this here, you have to go to the font page , switch to Geist Pixel at the top, and then scroll all the way to the bottom. This feels entirely too far away.) Show, don’t tell, generally. The Geist Pixel announcement feels rife for an avalanche of this, but has so little. I mentioned above wishing to see examples of manual refinements. There is a visual for “seamless mixing,” but it’s really a marketing photo, not a real-use example – it visualizes what, but you want to visualize what and why at the same time. I would love to see the spread of variants, specific examples of how the font is not “breaking in production” or not “scaling properly across viewports.” I don’t know what is a “semi-mono approach” and I would like to learn. Motion is okay, but it has zero nutritional value. If you have limited resources, don’t spend it on motion. Anything interactive is better. (But again, the best interactive thing is letting you type.) The “Already shaping what’s next” is a narratively unsatisfying section, as it promises stuff that you cannot see yet. Either show those, or skip the tease altogether. Fran Sans announcement post by Emily Sneddon (complements the font page ) – personal, distinctive, talks about the process, shows interesting artifacts. I feel that every small essay from David Jonathan Ross’s Font Of The Month teaches me something new – pick a font you like on that page, then click Notes next to it. Departure Mono font page by Helena Zhang doesn’t use a lot of words, but still tells a lot. Shantell Sans process post by Shantell Martin (complements the font page ) – already talked about it before .

0 views
Unsung Today

See you on the other side

A nice moment in Google Maps – if you are travelling by public transit, an indicator shows not just the current stop on the list in real time, but also exactly where you are in between stops: It’s not just something traditionally delightful, a cute big icon moving smoothly. I also think it’s very helpful. Travelling in a new place can be stressful and station names look similar; it’s nice to orient yourself without having to go back to the map, and if the indicator only always pointed to a station, it would introduce off-by-one errors (“was it the last station, or is it the next one”)? Plus, I think any design features that help your brain transition between abstract (list) and physical (map) views are very welcome. My suggestion would be to consider making it pulsating and/or blue to tie it better together with the current GPS position, which has had the same visual signature since 2007. #details #google

0 views

Weird projects I shipped with AI

Where are all the AI-generated projects? This is a common question from AI skeptics: if LLMs are so good at writing code, where is the tsunami of new AI-generated apps, services and games? I personally don’t find this to be much of a paradox. Writing code is only one of the bottlenecks involved in actually shipping a new product, after all. It’s also impossible to talk about the paid work I’ve done with AI (you’ll simply have to take my word that it’s increased my productivity). But one thing I can do is share a list of personal projects I’ve built with AI in the last twelve months. I definitely would not have done all of these by hand. I might have found the time to do one or two of them, but based on my pre-AI track record they would probably have stayed in the “GitHub repo with a few commits” stage. This list is a kind of existence proof : a bunch of weird projects, useful to at least some people, that would not have existed without AI assistance 0 . Most recently I’ve built skifreedle.com , a daily-game version of the classic Windows SkiFree game (i.e. “like Wordle, but for SkiFree”). The code for that is here 1 . I enjoy coding small web games by hand, but definitely would not have had the time to wire up all the different SkiFree objects or build neat features like a ghost of your fastest run. I also tried out a lot of different visual themes for the game UI before landing on something I liked. If I’d done this by hand, I would have only had time to try out two or three different looks, instead of fifteen or twenty. I’m very happy with how this turned out. I’ve been enjoying competing against my brother to get better times, since both of us have a lot of nostalgia for the original SkiFree game. Last year I built Autodeck ! I wrote a blog post about this before, but this came from my partner wishing there was some way to automatically generate Anki cards about random topics she wanted to learn about. It ended up being relatively straightforward to set up an endless feed of auto-generated spaced repetition cards: I set up Stripe payments for this one, more because I was worried about someone running away with my Groq balance than because I wanted to make money, but I was pleasantly surprised to see a bunch of people actually use this. Over five hundred people have tried it out, with enough paid subscribers to cover inference and hosting. I might have built this without LLM assistance, but I almost certainly would not have deployed it as a website. The hassle of setting up a database and Stripe would have just been too much work. I also built an AI-generated endless wiki . I wrote a blog post about this one as well. Like Autodeck, I was fascinated with the idea of non-chat interfaces for LLMs, and I thought a wiki-based approach where you interact with the model by clicking links was pretty cool. I learned the hard way that putting a LLM generation call on the end of a regular link was a bad idea: scrapers would exhaust my inference budget quickly. I ended up faking the no-article-exists-yet links with JavaScript, which at least so far has defeated scrapers. People still email me about Endless Wiki, and there are over 280 thousand pages generated. My original goal was to see if you could eventually generate a page for Neon Genesis Evangelion, starting at the root page and only following links (kind of like wiki golf ). I was successful! You can read the “Evangelion Anime” page here . Almost exactly a month after I launched Endless Wiki, xAI launched Grokipedia . Obviously they didn’t plagiarize me. This is a very easy idea to have, and my site was not the first infinite wiki (though I think it was the first one where you had to discover new pages by clicking on links). But it did take some of the shine off. I built a PWA that caches the VicFlora plant identification database so it could be used with low or no internet. This was more of a utility project for my partner, who likes plants and occasionally goes on field trips where internet is spotty. I would definitely not have done this without LLMs. It was reasonably difficult to scrape the basic dichotomous key from the VicFlora website: their API documentation was out of date, there were multiple possible pathways for fetching data (most of which were not functional), and the format of the data I did manage to fetch was hard to parse. I think I could have done it, with enough effort, but it would have been a substantial amount of work. I’m very happy with how this turned out. It’s not perfect, but it’s functional, and I’ve even had the occasional Victorian botanist email me with bug reports or feature requests, so it’s clearly seeing a little bit of usage. I did a bunch of other stuff that doesn’t necessarily rise to the level of a “deployed project”: my gh-standup GitHub CLI extension to automatically generate a standup report, which has just over a hundred stars, my (low quality) image geolocation benchmark , which I blogged about here , or my skill for extracting features from open-source models. There may not be a flood of AI-generated companies (yet), but at least for me there’s been a flood of small, weird projects that would not have existed without significant LLM assistance. I also want to shout out Simon Willison’s version of this , which is another great example of “weird useful tools that only exist because the cost of creating them was so low”. I did lift the spritesheet from DanielHough’s SkiFree.js , which attributes it to Wing Wang Wao . Of course, the original sprites and art belong to Chris Pirih’s SkiFree and Microsoft. I also want to shout out Simon Willison’s version of this , which is another great example of “weird useful tools that only exist because the cost of creating them was so low”. ↩ I did lift the spritesheet from DanielHough’s SkiFree.js , which attributes it to Wing Wang Wao . Of course, the original sprites and art belong to Chris Pirih’s SkiFree and Microsoft. ↩

0 views

17 down, 27 more to go

Here we go again. Afternoon walk this time around. It’s almost 2pm, and I’m standing in the same parking spot where I got picked up last week. No breakfast in me, but I did have lunch before heading out. Compared to last week’s hike, this one’s gonna be way easier. We have a bit more than 20kms to walk, with roughly 650 meters of ascent and 1300 of descent. Gonna be fun. Before we begin, I’ll have to apologise for the terrible photos I took, especially of the churches. Been a weird walk, and there was also non-ideal lighting most of the day and, I don’t know, I guess I was not very inspired today? I’ll hopefully do better next time around. Ok, down the mountain we go. Compared to last week, today’s a lot cooler, and there’s a gentle breeze. Perfect conditions to walk down a mountain and into the forest. The first part is a lot steeper than I thought. I did consider walking the whole loop in reverse, and this part would not be fun. I’m having a hard time going down, I can’t imagine going up from here, especially after a long walk. Thankfully, the trail becomes a lot easier to walk after that initial steep part, and I’m now enjoying my time in the woods. There’s gonna be a lot of going in and out of the woods on this hike. Down a few steps and I’m about to emerge next to Montemaggiore , the last town you encounter on your way up to Mount Matajur. I saw these concrete stairs many times before, while driving up this road. Always wanted to walk this trail, and I’m glad I’m finally doing it. The trail goes through the buildings, neat! So many old and abandoned buildings. Makes me kinda sad. But I get it, life up here ain’t exactly easy, or practical. And on the other side? A big, and frankly quite ugly, Jesus. This is relatively modern, and I’ll be honest, I’m not a fan. The trail continues in and around the town, down a few sets of stairs, and through some overgrown grass. So far, this one’s the worst-maintained trail I have walked. But I can imagine that doing maintenance is not exactly easy over here. So kudos to everyone who does it. I’m now back on one of those service roads, and it’s a very enjoyable walk. We’re slowly going down, the weather is lovely, and I’m very much enjoying my time outside. It’s not an incredibly varied walk so far, the scenery is mostly the same, but still, this is not a bad way to spend a Saturday afternoon. This small football field made me smile. I have no idea who comes up here to use it, but it’s fun to see it’s still there. I’d have loved to have something like this available to me as a kid. But now there’s nobody around, these valleys are slowly getting abandoned, which is sad but also understandable. Time to take a left turn, leave the service road behind me and go up into the woods again. I’m not too distant from the first of the three churches I’ll visit today. Was not expecting to find myself in this open area with a view of the valley underneath. This is a lovely spot, and there’s also a cabin nearby. Probably used by the hunters, like most of the cabins scattered around the valleys. Into the woods again, and I now need to leave the trail behind me because the first church is some 100 or so meters off the path. So down I go and to reach the church of Sant’Ulderico in Monte (15/44). The area around the church is quite neglected, and the overall scenery is really not the best. The only good thing about this one is that one of the windows was open, and I finally managed to take a decent picture of the inside of one of the churches. It’s very barebones, but the altar is nice. I still think it’s sad that these are all locked; they could become awesome shelters for the night. I should probably learn lock picking… Time to hike back up to where I left the trail earlier and keep moving on through the woods. Next stop is gonna be where the trail splits and the variant starts. I hate these variants. They make no sense, and this one in particular is almost as long as the main path, only to touch one more church, one that is not even all that nice or unique. But I guess there’s no point in complaining; nobody is forcing me to walk these trails after all. After another hour of uneventful walking into the woods, we have finally reached the location of the church of Santi Primo e Feliciano (16/44), which I didn’t even bother visit closely and only took this photo of it. The only nice thing about this church is the location, since it has a great view, but the building itself is incredibly uninspiring, especially because it has probably been restructured in modern times. There are a couple of pictures on the link above if you want to see more of it. That said, though, there are a couple of way more interesting things to see around here. Starting from this throwback sign. What a blast from the past. Do you even remember the time when not everyone had a phone, and you could use phones in public places to make phone calls? What a time! The other unusual sight is a Thai temple?!? That was very unexpected. I was aware that there’s a Buddhist one in the area (that I might actually see during a future walk), but knew nothing about this one. Neat! And right in front of it? A small chapel to remember the people who have died during the two World Wars. I told you these things are everywhere and that it’s impossible to forget what happened during those times if you live here. I’m about to start going back up, and my watch vibrates to alert me that there’s a “severe weather storm alert” which is always great to see when you’re half way through a long walk, and you’re in the middle of nowhere, a couple of hours from your car. I was contemplating stopping for a quick break, but the opposite happened: I started going! As a result, I took very few pictures of the next chunk of the walk but was mostly the same, going in and out of the woods. An hour later, I connected back with the main path and was close to the third and final church of this walk, the church of San Michele Arcangelo (17/44), which is by far the best one of the three I visited today. Very small and in an elevated position with a nice view of the valley. Like many others, it was reconstructed after the quakes of the sixteenth century, and you can clearly see that there are parts missing in the front. Still, I much prefer to see churches in this condition; at least they feel like they have a history behind them. Almost 4 hours in, still 7 kms left to walk, which means there’s no time to waste. I leave the church behind me, go through the small village, into the woods, and some 20 or so minutes later, I’m back on a paved road. My legs would love to go right and downhill, but I need to go left and uphill, the direction our grandparents used to take to go to school back in the day. But I’m quite happy for this because I get the chance to see a church I've always wanted to visit. That’s the church of Sant'Andrea Apostolo and every time I drive up to Mount Matajur, I can see it from my car. Has this very characteristic twisty road going up to it from the small village underneath and I always wanted to drive there. And I have to say, the location is very nice. Living up here must be a nightmare because there’s only one way down, and the road is very narrow. Plus, the closest anything must be 30 to 40 minutes away by car. But if you’re looking for peace and quiet, this is the place for you. Another point scored by team Mary on my way out of the town. There’s less than 5kms left now, I’m starting to feel a bit tired, my feet hurt, I need better shoes, I want to take a quick break, but I also don’t want to stop because I know I’m only an hour or so from my car. And stopping at this point makes no sense. So into the forest I go again for the final stretch of this walk. Found a bunch of sheep chilling in the forest right at the end of the walk. These animals are so fun. If I had the space, I’d totally get a few, just to see them play with the crazy dog I have. I wonder what he would do. Maybe one day I’ll find out. Final descent into the forest, couple of turns, and here we are, back at the parking spot where I left my car some 5 hours ago. Thankfully, it’s still there because walking home from here would not be fun. Doable, sure. But not fun. So far, my least favourite walk and the churches were mostly uninspiring, but hey, that’s part of the fun. Next up, we have another long descent, but I very much look forward to doing the walk after that, number 6, since that has the potential to be the most tiring yet fun and interesting walk of the entire series. I’ll definitely need to buy a better pair of shoes by then. There's a new folder on the shared Drive folder with all the pictures I took on the walk. And yes, I have moved from iCloud to Drive because apparently iCloud doesn't like huge albums. And there's also the link to the data recorded by my watch , if you want to get more info on the hike itself. You love the outdoors and RSS. You're one of the special ones.

0 views
ava's blog Yesterday

rose ▪ bud ▪ thorn - may 2026

Reply via email Published 31 May, 2026 It was my wife's birthday, and our wedding anniversary! Baked some cakes and had a great time. Mine is the Donauwelle attached at the bottom of the post, my wife baked the fruit cake. My friend who visited Japan bought us great gifts from there; I got two gachapon (Cinnamoroll and My Melody), some matcha and My Sweet Piano chopsticks. I finally have it in writing and it's been communicated officially that I am my department's data protection coordinator now. I blogged more. I bought myself a big Build-A-Bear Usahana and a tiny one for my bag. Also, new matcha and I restocked my skincare and supplements :) I feel spoiled by myself. I'm having a great time at the gym, going 3 times a week, and incorporating the strength machines now. The added muscle/strength really helps with posture and counteracting the desk sitting. I'm making good progress. I reduced negativity from my online space. I went to a protest for ME/CFS! I have been better with keeping up with emails. Anita, if you are reading this, I cannot reply to you because it says sending key is not valid. We have a bread cutting machine now! Makes it easier to cut the bread my wife is baking for us :) I attended CPDP 2026 in Brussels. I reached Magenta status (35+ translated cases) as a Country Reporter for Noyb. Working on better eating behaviors and no guilt during rest. I am working on slowly booking cool classes and activities for the next few months. Been struggling with my face shape. I have chubbier cheeks anyway naturally, but whenever I need a round of Prednisone or I am stressed or there's hormonal stuff going on, they get bigger (cortisol, water retention). They are bigger lately... definitely a source of discomfort and shame when we live in a time of razor sharp jaws and almost-hollow cheeks. I will now have to do my injections weekly :( Dienogest doesn't work at all for me. Instead of preventing periods, it causes me more of them. Had to get off of it. My soy and rapeseed sensitivities have been extra annoying lately. Can't eat my beloved tofu, and they put rapeseed oil into almost every protein-rich vegan replacement product. I love my lentils, peas and beans, but occasionally I just wanna have some banger vegan köttbullar, schnitzel, or burger patty without a rash, man, or not make everything myself. Not to mention restaurants, or the fact that they drown everything in rapeseed oil based condiments... I haven't been studying nearly as much as I should. Having some issues with the modalities and feeling a bit stressed, like I need more time away from it. I've been very ambitious this month with my blog posting, and it has caused some writer-constipation at times. I had all these drafts ready with some links and loose thoughts already collected, and wanted to write them out fully; but because I set myself arbitrary deadlines or a loose " This needs to be finished and published today! " I felt intense pressure, which made me freeze up... it's really not that serious, but I made it so, for some reason. I also frequently felt stuck between 2-4 equally "important" tasks, posts, topics, whatever, and when I started one I looked at the other and switched, progressing at nothing. Terrible cycle. I moved some planned posts to June and eased up a bit. The menu of my favorite café has been severely reduced and worsened. Also cannot believe that I am paying 10 Euro for a wrap now. The Brussels trip was filled with some disappointments and stress.

0 views
Unsung Yesterday

“In a world of unresponsive 911 calls, it is the 912 that actually works.”

I know I just mentioned the Google Search app, but I’m also in the process of disentangling myself from Google and Gmail after last week’s Google I/O revelations. On that note, this is an interesting, meandering essay by Ernie Smith at Tedium , reflecting on the enshittification of Google and the two-year anniversary of &udm=14 , a simple site that removes AI from Google’s search results: I spent two hours of my life building a thing. Google has probably spent thousands, if not millions, of collective employee hours building all their AI innovations. And for a surprisingly large number of people, the two-hour workaround I built wins out. There’s a lesson in that. Somewhere in the middle, the essay transitions into talking about the value of good tools and single-serving websites: Our world needs more, smaller tools that speak the same language, where everyone makes a little money, but nobody dominates the industry. In the 1980s, the software industry was kind of like this. Oh, sure, Microsoft and Apple were still out front, sucking up all the oxygen. But there were lots of little companies, selling software on disks. The bigger ones put them in boxes in stores. The smaller ones realized that they could just ship software through the mail and let the software spread naturally among user communities. Shareware didn’t really survive the internet era—but, at least for a while, its spirit did. More recently, that spirit has taken a backseat to the larger companies that realize, if they’re big enough, they can shape how we interact with our world. In 1991, if you wanted to start a software company, you had to hope that your product was good enough that word of mouth and a P.O. Box could push it around. That’s exactly what happened when Tim Sweeney released ZZT. It became the starting point for Epic Games, the kind of company that today is big enough that, thanks to its Unreal Engine and the success of Fortnite, it can dictate terms to much of the gaming industry. If you ask me, I want a world where more software is like ZZT than it is like Fortnite, because more people have a chance to succeed in the former environment. Previously in this general category, we covered Keyhole and (Gmail) Simplify . If you have a favourite small tool or a simple tool-like website, I’d love to hear from you! #ai #enshittification #google #toolmaking

0 views
Unsung Yesterday

“Nemo? That’s a nice name.”

Do you know those “Are you still here” screens? In some cases – like banking – they are ostensibly there to improve security. In public transit ticket or similar machines, on the other hand, they exist just so the machine can easily reset itself ahead of a future customer. Resetting to default state happens on your phone, too. I’ve been thinking about it this past week and found a few examples. The Google Search app comes back how you left it, except if you abandon it for longer than 45 minutes. If that‘s the case, it returns to a pristine, deterministic homepage. (You can always come back to the previous session, though.) When you pause a podcast or music, at least in my setup, it will be on the home screen for 5 subsequent minutes – you can then resume it by simply tapping on your AirPods. But leave it dormant for longer than that, and the home screen forgets about it and resuming is impossible: = 3x)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/3-framed.1600w.avif" type="image/avif"> = 3x)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/4-framed.1600w.avif" type="image/avif"> My favourite: if you swipe through the apps back and forth on the iPhone in a touch UI equivalent of command-tabbing, there needs to be a specific moment where the app you switch to becomes the “current” app. On desktop, it’s easy – you can reset the state at the next invocation of ⌘⇥. But there is no such obvious moment on mobile. When there is no obvious moment, timeout can be a great answer. And so it is here, and it seems to be set at about 5–6 seconds: I understand the Google Search and the app switching examples. But I am not sure I know why a podcast resets so soon. A challenge when talking about this without seeing the code – as it is with many things on Unsung – is that I don’t know if this is carelessness, a technical limitation, a design consideration I’m unaware of, or just something that’s intentional, but I happen to disagree with. Even testing this has been hard if the delays are longer than seconds. The extra challenge for Google Search, as it is for many other apps, is that they also reset when iOS itself purges it to make room for other apps. This isn’t great, and can be avoided if you care; we talked before about Bear and how it remembers its precise state even after the system evicts it from its memory. Whether you want your app to remember you forever, or whether you want some deliberate forgetfulness, it could be important to take ownership of that. My go-to example of a miss in this category is Google Maps, which completely throws away its current trip-in-progress status whenever the iOS kicks it to the metaphorical curb – and returning to that status later again as a user is a really complicated sequence of steps including rewinding back the time, on top of travel already being stressful. = 3x)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/6-framed.1600w.avif" type="image/avif"> = 3x)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/7-framed.1600w.avif" type="image/avif"> By the way, I can think of fewer examples on the desktop, but that makes sense given desktop apps are less tactical, and given that ⌘Q exists. But Spotlight does freshen itself up after about 7 or 8 minutes… = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/8.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/8.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/9.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/9.1600w.avif" type="image/avif"> …and Raycast actually offers an option to set its short-term memory from between 0 seconds and three minutes, with the default being 90 seconds: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/10.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/nemo-thats-a-nice-name/10.1600w.avif" type="image/avif"> #details #google #interface design #raycast

0 views
Giles's blog Yesterday

On first looking into JAX

Much have I travell'd in the realms of gold, And many goodly states and kingdoms seen; Round many western islands have I been Which bards in fealty to Apollo hold. Oft of one wide expanse had I been told That deep-brow'd Homer ruled as his demesne; Yet did I never breathe its pure serene Till I heard Chapman speak out loud and bold: Then felt I like some watcher of the skies When a new planet swims into his ken; Or like stout Cortez when with eagle eyes He star'd at the Pacific -- and all his men Look'd at each other with a wild surmise -- Silent, upon a peak in Darien. John Keats, On First Looking into Chapman's Homer I've been working with PyTorch quite a lot for the last couple of years, and feel like I've come to a reasonably solid understanding of how it all fits together. Working through Sebastian Raschka 's book " Build a Large Language Model (from Scratch) ", training my own LLMs locally and in the cloud , rebuilding Andrej Karpathy's 2015-vintage RNNs -- over time, it all adds up! But, of course, there are other frameworks, and one I kept hearing about was JAX . While it's less dominant than PyTorch, it has a reputation for a certain cleanliness, a certain purity. And having spent time over the last couple of weeks working through the tutorials, and translating small PyTorch examples into it, I've been really impressed. In this post I want to give an overview -- to report back to beginners like me, still living in PyTorch-land, on my new discovery. Less like Herschel discovering Uranus, and more like a 16th-century European coming back after having discovered something that the people who lived there were perfectly well aware of. What is this JAX thing, and how does it differ from PyTorch? I think that the main differences between PyTorch and JAX are something like this, but a little less strident: Having overstated my claims, let me dig in and perhaps walk them back a bit. Once I've gone through them, I'll do a walkthrough of porting a simple PyTorch training loop to JAX, which should illustrate the points well. Finally, I'll wrap up with the counterargument. JAX is wonderful and shiny, and 30+ years of industry experience and cynicism makes me fear that it might be doomed :-( But let's start with the positive! [Happy face on.] A simple example that nicely contrasts the different philosophies of the two frameworks is what the core of a training loop looks like. Here's how you might write one in PyTorch: This is kind of mechanistic. You're telling the computer what to do, step by step: Now let's look at a parallel JAX implementation: It's clearly very different. No explicit backward pass, no gradient-zeroing, and the forward pass and loss calculation are baked into a separate function. But why is it shaped that way? Let's think about what we're actually doing in our training loop. The gradients are the partial derivative of the loss function ℒ against the weights W : Now, I'm being a bit sloppy with that notation, because ℒ is a function, and it -- in the mathematical formulation -- takes the weights as a parameter. So it would be better written like this: But that's still not quite right. In a real training loop, we're doing this in the context of a particular input batch, X , and its associated targets, Y . 2 We might write that mathematically as this: ...where you can read the colon as "given". Now let's look again at the JAX code to work out the gradients: That's an almost-perfect mirror of the maths! The function takes a function , and returns another function, , which takes the same arguments. When you call , instead of returning the result of , it will return the derivative of with respect to its first argument, given the values of the others. 3 How is it doing that magic? Let's look at a simple concrete example: If you do the initial call to : ...then it just wraps in a helper function. It's when you call that the magic happens. ...will print out this: The first parameter -- the one with respect to which we're asking for the derivative -- is replaced by a object. Because it's wrapping a float, it can be used like one, so the function executes as expected. But it also keeps track of what happens to this variable as the code executes, and essentially builds up what in PyTorch would be represented by the computation graph. So: while in PyTorch, the variables that you pass in to a function that you need gradients for need to be special PyTorch objects that can keep a reference to those gradients -- the parameter that pops up frequently in PyTorch code -- in JAX, it's all handled by variables being automatically wrapped in these special tracers. Once it has the results of the function as a whole, including the chain of operations that was traced, it can automatically do a backward pass, and we're done. That's really nifty! Now, the example above was a toy one, with just one parameter. In a real training loop, you're differentiating against a set of weights, and those will be something more complex. But handles that gracefully. Let's see what happens if we pass in an array as the first parameter: So, we've got partial derivatives with respect to the elements of the array that was the first parameter -- just what we'd need for a single-layer neural network without bias. But what about something more complicated? For something like (say) an LLM, we have quite a lot of structure to our weights: our input embeddings, output head, all of the layers with their attention and feed-forward weights, and so on. handles that by understanding basic Python structures -- things that can be mapped to what JAX calls PyTrees. PyTrees are nested tree structures of dictionaries, lists, tuples and so on, where the leaves are numbers or JAX arrays 4 . If you ask for gradients of a variable that can be represented by a PyTree, you get them back in a form that mirrors that PyTree: If you combine that with JAX's tree-aware function, you can combine those gradients with the original parameters to update them as you train. I'll show you how that works later on, when we go through an example of porting some PyTorch code to JAX. So, all of that cool stuff was made possible by the tracer objects, which are passed in instead of the real parameters, and keep track of the computation graph (just like the graph that PyTorch attaches directly to the variables). But tracers are more generally useful than that; they really come into their own with the next JAX difference: the JIT. Imagine that you've built some kind of nifty model in PyTorch. As part of it, you do a calculation something like this: You decide that this is generally useful, so you code it up as a CUDA kernel and make it available to the community, like Erik Kaunismäki has with his "MaxSim" kernel. Maybe later on, it will get added to the PyTorch library as a standard component. There are a lot of optimisations like that built into PyTorch; people found that there were higher-level abstractions on top of basic tensor operations that were generally useful, so they coded up lower-level optimised versions. For example, in the LLM I've been working with, there is an implementation of LayerNorm . But PyTorch has its own one built in . And there's a CUDA implementation that it will use automatically if it has the appropriate hardware available. There is a problem, though. Imagine that someone else is working on a different kind of model in the future. And for reasons completely unrelated to the MaxSim calculations that Kaunismäki nicely optimised, they happen to need to do the same calculations. Now, there are two things that can happen from there: The first is not ideal; but the second isn't great either, if what they're using it for is not a MaxSim operation in reality, just something that happens to look the same mathematically. In the general case: all optimisations that get into PyTorch have to be carefully named so that they reflect the exact level of abstraction that they're targeting. And when people are writing PyTorch models, they need to actually know which optimised abstractions are available, and where to apply them. Now let's look at JAX. It has an innocuous-looking decorator, , and you can use it by adding a single line before your function: Behind that single line is a huge amount of useful infrastructure. Just like , it's a function that takes one function and returns another, without necessarily running the underlying code. 5 But when you call the wrapped function for the first time, some impressive stuff happens: This will essentially execute the code twice: The first time through, it will create another of those tracer objects; this time, though, it won't wrap the number -- it will just know that it is a wrapper for a float. It will call the Python code with that tracer, and all of the operations in the function will be run, but the result that comes out at the end will essentially just be a representation of what calculations were done in an abstract sense -- like the computation graph that was used for working out gradients, but without specific numbers in it. JAX has a nice way to display these representations as what it calls JAXPRs, and the JAXPR for that function's representation when called with a float parameter will look something like this: That JAXPR can be compiled into the appropriate code for the platform where you're running it -- x86 machine code, compiled CUDA, the equivalent for AMD or Google Tensor Processing Units (TPUs), and will be cached. The key for the cache will be meta-information about the parameter -- in this case, something like "a 32-bit floating-point scalar". Next, the compiled code -- not the original Python -- is run with the actual value of the parameter, the that we provided. Now, of course, the advantage of doing this is that when you call it with a different floating-point number -- say, -- then you don't need to do the compilation again. You can just rely on the cached version. And the fact that the compiled code is cached based on the metadata means that if you call with a vector, then it will compile a new version for that, and likewise for a matrix version. 6 This is all really nifty, and you can see how it would help right away. But for me, at least, an excellent extra benefit is how it can save people like Erik Kaunismäki the bother of writing custom kernels. The compilation that happens, taking the representation that it got from the tracing process and turning it into backend code, goes through an optimising compiler, XLA . And that compiler can recognise "standard" operations and combine them together. This won't be at the level of "standard operations" like MaxSim, of course -- more, "this looks like a convolution, let's use the standard kernel". But it does mean that instead of someone having to take code written in Python and hand-port it over to CUDA to get a GPU speedup, the same expertise can be put into improving the optimisation part of XLA to get a speedup for all code. That's pretty amazing. However... If you want something like the JIT to work properly, you need to limit the kind of code that it works with. In particular, it needs to be functional. A function must always return the same value when given the same inputs -- so this is fine: ...but this will cause problems: ...because could be changed. Specifically -- because the global had the value during the initial traced run of the function, that value will essentially get hard-coded into the cached JITted version, so both prints in the second example will output . Something slightly surprising comes out of this -- something that makes JAX code look very different to PyTorch. How we handle randomness needs to completely change. Consider this code: As a whole, it's deterministic. But it breaks the functional requirement that the function can only depend on its inputs. Both calls to take the same input, but they return different results. Even worse, if we were to do something that consumed randomness between those two calls to , for example: ...we'd get different results. The state of the random number generator is global state kept outside the function, just like in the example above. A naive solution to this might be to make the state of the RNG explicit as a variable -- you can imagine a library that worked something like this: That looks more functional, but when you think about it, we haven't actually fixed the problem. We're passing the same variable in in both cases, along with the same number, but we're getting different results. It's not global, but it's still mutable behind the scenes. What you'd actually need to do to make it purely functional would be something like this: The function is generating a new random integer and returning both that and the new state of the RNG, then we pass that back along with our result. We've made the random state variables immutable, and so it's functional. But the API is getting pretty ugly pretty quickly. So JAX does something that is equivalent, but a bit cleaner. There's a concept of a key , which needs to be passed into any function that consumes randomness: That's kind of like the that we have in the first version of the code above. But it's immutable; when you use it, like this: ...it will not be changed, so no matter how many times you call it with the same key, that function will return the same value. (Note that takes an inclusive lower bound and an exclusive upper bound, like Python's , but unlike the stdlib's . It also needs to know the shape of the result -- for a scalar, for a 1x2 array, and so on.) If you want it to "move on" to a new state, you use the function, which takes an existing key and returns two (or more) new ones. So you can do something like this: Now, that and stuff is a bit ugly, but while it's not OK to mutate the contents of variables in functional code, it's absolutely fine to assign a new value to an existing one, so what I've found myself doing is writing stuff like this: However, there are more powerful ways to use ; I'm not confident enough at using it yet to go into that, though, so I'll hold back for now. I suspect (assuming I keep using JAX) I'll be posting about them in the future. OK: so the JIT means that we have to write functional code, which makes things a bit fiddly -- no more global state. And that has a surprisingly big knock-on effect with randomness. But there's another thing that comes out of the JIT and the way it does tracing. It's not a functional thing (though some of the docs seem to almost be treating it that way), but is caused by the same kind of constraints. It's not part of my four theses above, but I think it's important enough to call out in its own subsection. Imagine this function: It's purely functional, so no problem there. But let's think about what the JIT is trying to do. It wants to convert the function into a simple sequence of operations, so it will create a tracer for a floating-point scalar, then call with it. When it hits that statement, there will be a problem. The tracer is meant to represent any arbitrary float, so should it take the branch or not? There's no good answer. It doesn't know which branch to follow -- whether the sequence should be "square it and return the result" or just "return it directly" -- and will fail with a somewhat obscure error message: So this gives a hard constraint on functions that you want to JIT: by default, they can't base control flow on the values you pass in. There is a workaround -- but it comes with tradeoffs. Let's take a slightly sideways route to explain it. Firstly, although you cannot do control flow based on the value of a parameter -- which the tracer doesn't know -- you can base it on other information that actually is stored in the tracer. Let's say that we called like this: The tracer that would be passed in when trying to trace the function would be something representing a 2x2 array. The shape of the parameter is part of the tracer, even though the values aren't. So you could do something like this: ...and it would work. It's worth thinking explicitly why this is. When you call a JITted function, it will create a tracer that contains information about the type of thing you passed in as a parameter -- scalar versus array, and if it's an array, the array's shape. It then runs the function with the tracer, gets the sequence of operations, compiles them and then stores the result in a cache keyed on the metadata -- type and, if appropriate, shape -- that it used to create the tracer. So when we call that function with a 2x2 array, we get a 2x2 array version, then if we call it later with a one-dimensional array of length 2, we'll get a new version for that. One workaround for basing control flow on values is essentially to tell the function that it should treat the values of a particular variable as being like the metadata used for this cache keying: it should compile a new version for each value it sees, rather than just using the metadata. It takes a parameter , and a matching , which tell it which parameters to do that with. So, this will work: (Remember that the thing after the for a decorator needs to be a function that returns a function, so we have to use to "inject" in the extra argument.) However, the downside is pretty clear: every time we call with a new value, it's going to have to JIT a new version of the function and cache it -- that's going to be slow and take up memory. So, as an alternative, we can use the package . This provides more functional-looking alternatives for control flow, which are compatible with the way the JIT works. For example, there's a function, which we can use to replace s: That feels a little bit like a workaround, but it does solve the problem. How? Well, it's worth checking the JAXPR for it: What's happened here, I think, is that the JIT has recognised the call to as being a primitive function in its intermediate language, so has just kept it in there. It couldn't do that with the because when it was tracing, all JAX itself saw was what was happening to the tracer -- there was a boolean comparison, and then the stuff in the chosen branch happened. The fact that there was an there happened in Python itself, outside JAX, so it was "invisible" to the trace. That feels a little inelegant to me right now, and I'll come back to it later. Let's move on to the final difference between the two libraries that I want to cover: JAX's relative minimalism to PyTorch's more maximalist approach. I think the smaller size of JAX -- at least in terms of its API, if not in terms of the JIT and XLA magic under the hood -- compared to the sprawl of PyTorch is not entirely unrelated to the JIT being at its core. PyTorch, after some initial design, has almost been forced to grow organically; JAX feels more carefully designed, so it doesn't have the same need to grow (though of course it can). The reason for PyTorch's growth is, at least in part, because it needs to absorb optimisations. If something is slow, someone needs to write a CUDA kernel for it. If there's a CUDA kernel, it needs an API. And if it is generally useful, that API becomes part of PyTorch. Multi-head attention? There's a class for that . SELU? Yup . Very specific softmax approximations based on a paper published in 2016? PyTorch has you covered . By contrast, JAX doesn't even have linear layers or optimisers in the framework itself; if you want to use them, you can write them yourself (contraindicated), or you can use libraries built on top of JAX , like Flax for common neural network components and Optax for optimisers. This feels like a nice division of responsibilities, and it also seems like something that would have been very hard without the JIT. So while the JAX core may well grow in the future, the design it has now puts it in a good position to grow in a more planned, well-designed manner -- rather than having to grow to absorb more and more abstractions just to keep it fast. Those abstractions can more easily sit in libraries written on top of JAX. That's the 10,000-foot overview; four (or maybe four and a half) main differences between PyTorch and JAX. It's more maths-y, JITted, functional and minimalist. What does that actually mean when you get down to coding with it? Let's get into the weeds with an example. Let's use a really simple one: training a neural network with two inputs and one hidden layer to calculate the XOR function. The code is in this GitHub repo , but I'll put the relevant bits here in this post. Firstly, an idiomatic PyTorch implementation: If we run that, it trains a solid-looking model in about four seconds on my machine: Now, if we're porting to JAX we need to do something about the fact that JAX doesn't have optimisers and the neural network stuff built in. If this was a real codebase, we'd almost certainly do that by using the libraries built on top of JAX, like Flax and Optax. But for this toy example, I think it's more illustrative to strip down the PyTorch version so that it uses fewer parts of the API -- essentially so that it only uses the stuff that JAX has -- and then to port the result. The optimiser first. The code is here but the diffs are pretty simple. Instead of creating an optimiser, we just specify our learning rate: Instead of zeroing out the gradients using the optimiser, we can just ask the model to do it: And instead of stepping the optimiser, we call a new function passing in the model and the learning rate: The function is simple enough; we just switch into mode so that PyTorch doesn't try to track the computation graph (working out gradients for applying gradients and triggering some kind of crazy gradient-ception), then we just iterate over the model's parameters and follow the normal SGD process, subtracting the gradients times the learning rate: Running that on my machine actually works out slightly faster than the original 7 ! It's also quite nice to see that (within the bounds of the printing precision) the loss and the final results are identical. OK, so now that we've got rid of the optimiser, let's do the same with the s. Here's the code , but let's do a quick walk through the differences. Instead of creating an , we will just generate an array of layers: Zeroing out the existing gradients will also need to be done on those layers: ...and likewise our loss calculations and the function will need to use them: We used a couple of new helper functions there; this one generates the initial weights for the layers (based on the docs for ): Note that each of the tensors we created, the and the need to be explicitly told, using , that we're going to want PyTorch to track gradients on them. Zeroing out the gradients is just a case of chugging through each layer, and then for each setting the weights' and the biases' gradients to : Now, to calculate the loss, we're actually not changing much. We had this: ...and now we just change it to this: That is, we've added on a new function to do a forward pass through the given layers with the given parameters. That looks like this: Standard NN stuff . A quick tweak to use in the printing of the results at the end: ...and we're done! Let's run it: Even faster! Sounds like there aren't any nice pre-baked optimisations in that part of PyTorch, then... But again, within the bounds of our precision, that's exactly the same numbers as we got from the original PyTorch version, which is very reassuring. OK, now that we've got something that's kind of JAX-shaped, let's port it over. I think it's worth showing all of the code for that (though it's here on GitHub if you want to view it there), and then I'll highlight the important diffs separately. If you look at it side-by-side with the previous PyTorch implementation , you'll see that it's really similar! Running between them makes them look more different than they are because of the extra threading through of keys that we need to do in order to satisfy the strict constraints on random number handling in JAX, (and of course there are function name changes like becoming and becoming ). But the important changes are much smaller. Firstly, weights and biases no longer need to know that we'll want to track gradients for them, because that's all handled by the tracers that JAX wraps around them: Relatedly, the function that iterated over the layers and zeroed out the existing ones is completely gone. Because gradients are now stored on tracers that wrap around our parameters rather than on the parameters themselves, we don't need to zero them out. The step function is still there, though, but it's much simpler. Before we get to that, let's take a look at the way we're getting the gradients for it, in the main training loop. Here's the diff: Hopefully the change there will be nice and familiar from the start of this post: we've moved from the PyTorch procedural "do a forward pass then do the backward pass" to the JAX maths-y "work out the gradients for this function". is a utility function that does the same as the we encountered then, but rather than just returning the gradients, it also returns the value of with the given parameters, which is useful for our logging. Now, remember that is a list of dictionaries, something like this: And also remember that -- and likewise -- have that smart trick where they return the gradients in the same PyTree structure as the parameter that we're taking the derivative with respect to. So will also be a list of dictionaries, each of which has and . Now, as I mentioned earlier, JAX has a useful function called . Like the Python function that maps a function over one or more lists, JAX's version maps a function over one or more things with the same PyTree structure. So, because and have the same structure, our function can just use it to apply simple gradient descent like this: Very clean :-) That's it! A full JAX implementation of our toy example, and when we run it: ...it works! So, let's move on to... Yikes. It was almost 30 times slower than the PyTorch version. But then -- we did all of that work to port the code over to JAX, which is great because it has a JIT, and then we didn't use the JIT. Whoops! Adding a few calls to helps. If we add them to the , and function then we get this code , which is faster: ...but it's still almost eight times slower than the PyTorch code. How can we make it faster? Well, perhaps we can do more if we put more of the loop into the JITted stuff. Right now, the core of our training loop looks like this: and are JITted. But what happens if we try to JIT a larger step? We can move the forward pass and the step into a JITted function on their own: ...and then call it in the loop like this: With that, all of the JAX code apart from input and target wrangling is moved into a JITted function. We get this code , and running it gives us this: Woohoo! Almost 45% faster than the PyTorch version :-) So: porting to JAX alone gives us nice maths-y code, but we need to JIT it properly to get performance that matches PyTorch. (The fact that it's faster than PyTorch in this case is not something that I think you could rely on -- this is, after all, a toy example.) It's also an interesting indicator that you actually need to think about what to JIT. My initial thought, "just whack an on the inner stuff", was not enough. We needed to do more than that. I've just had an interesting chat with Claude Opus 4.8 about that, though, and will probably post more about it later. For now, I think a useful rule-of-thumb is to wrap stuff in at as high a level as you reasonably can, to maximise coverage. So, this completes the happy part of this post -- I've shown what it can do, how nicely it maps to the maths, and how it's (relatively) easy to make it fast. What are the downsides? Another deliberately overly-strident heading ;-) I've been programming for more than 40 years, and working professionally in the tech industry for more than 30. I'd like to feel that this makes me a better engineer than I was when I was first starting out, but I can confidently say that it has made me a much more cynical one. Over that period, I've come to categorise new APIs, languages, and tools into three approximate groups: godawful hacks, solid but not overly inspiring engineering, and things of beauty. They're loose categories, and most things are somewhere between one and another. But I think they hold reasonably well. My cynicism and experience tells me that: When we were building our programmable spreadsheet, Resolver One , some of the team pointed out that a functional language -- specifically, Haskell -- would be a better fit than Python. It was a tough decision to stick with Python, and I'm still not 100% sure it was the right one. But I do remember having sales meetings with quants at various financial firms about it, and in those meetings, some of the potential customers also suggested a Haskell port. I'm not saying that there's a perfect correlation between where we heard that, and the later notes in our sales status spreadsheet saying "client being acquired by a non-bankrupt competitor, all expenditure on hold" during the 2008 financial crisis. But I'm not not saying that either. If you've read this far, you can probably tell that I see PyTorch as solid engineering, and JAX as closer to a thing of beauty. Maybe it's just the cynicism of age, but let me try to articulate the things I worry might put JAX into the "beautiful but doomed" side of the "beautiful" category. Firstly, I'm not convinced by the way that JAX, with its JIT, requires you to try to write Python as if it were a functional language. It's easy enough to see that this isn't functional: ...but harder with this: Even worse, the way that tracing works means that you have even more constraints than "just" being functional would require -- remember this example from earlier? Python is not functional, and is deliberately so. Trying to make it so is always going to lead to weird bugs (for example, how the value of the global on the first run would be baked into that function) and hard-to-understand error messages (you really need to be clued-up to work out what means). The package -- for example, the function we used to work around the fact that JAX could not "see" the Python way back in this post -- feels like a bit of an ugly workaround. Python has control flow functions, but they don't work with the JIT's tracing, so we have to re-implement them in JAX. Hmmm. Now, I've written extensively above about how JAX's restrictions, however confusing, enable a lot of the amazing stuff that wouldn't be possible in normal PyTorch. What if there were some way to write PyTorch code and compile it directly to something that can execute on the hardware? It turns out that as of 2023, there is: . From what I understand, you're meant to be able to just attach it to your code and it gets JITted. But unlike JAX, you don't need to restrict the code you write. I've not investigated in much depth (after all, this post is already absurdly long and has taken more than a month on and off to put together), but it looks like it handles stuff that can't be compiled by using a concept of a "graph break" -- that is, it happily JITs what it can, then if it hits something that it can't JIT, it will cache the "work so far" as one compiled unit, run the Python code for the unJITable stuff, then (when it can) drop back into JIT mode. The best of both worlds? I don't know, and would need to spend much more time investigating in order to learn. But I can say that for my minimal-effort port of my toy XOR code , following the structure of the JITted JAX version, it really did not help: For those who are keeping track, that's slower than the uncompiled version, which came in at about 3.5s. And the issue doesn't seem to be an up-front cost of JITting that would be paid off if we ran for more epochs -- each individual "Loss at epoch XXX" print comes out slower. Again, for the sake of sanity I'm not going to dig into it further, especially given that this is a tiny toy model and probably about as far from the target use case of as you can get. But it's something well worth noting for the future. Stepping back: one other way of looking at this is that Python might just be the wrong language to try to build code that compiles to GPUs. I'm learning JAX right now so that I can re-implement my existing LLM from scratch project in something other than PyTorch, to make sure that I really understand it. I asked people on X/Twitter for votes or ideas , and while JAX won, Jeremy Howard suggested Mojo . Mojo is a Pythonic language that compiles directly to CPU or GPU code, so it explicitly only contains features that can be ported that way. Unfortunately, it's lower-level than I really wanted for this project (and, importantly, does not have built-in autograd support). But if it did -- if, for example, there was a library like JAX for it, perhaps it would be better than using Python as the foundation? I've looked for something like that, but to no avail. Some work-in-progress projects, but nothing ready for use. At the end of the day, I think further experience is essential if I'm going to come to a solid opinion on JAX. Experience with other tools can only get you so far, and it's easy to fail by pattern-matching what you're looking at with things that you've seen before, especially when you're old and cynical. All I can say at this point is that JAX is making my "beautiful but doomed" spidey-sense tingle. 8 The title of this post is important -- it is my impressions on first looking into JAX, not the considered thoughts of someone who's spent months or years working with it. I've only scratched the surface, and haven't even touched the larger JAX ecosystem, or indeed its powerful handling of memory sharding for multi-GPU or even multi-node setups (which may well be one of its biggest advantages). My next step is going to be to implement a GPT-2-style LLM in JAX, probably using Flax and Optax as helpers, and perhaps by the time I'm done with that I'll have changed my views. But at this point -- after working through the tutorials and porting some toy models to get at least an initial feel for it, I've come to the conclusion that I like it. The question is, do I like it like I liked Python when I first came to it -- "this thing is really neat and clean, even if it has flaws" or is it more like I liked Haskell -- "this is a stunning thing of beauty and is completely doomed in the real world"? Time will tell. But in the meantime, if you've been working with JAX for some time and want to counter any of the points I made, if I've completely misunderstood anything, or if you have any corrections, then please let me know! After all, explorers in areas new to them are prone to making mistakes from time to time... The forest of Skund was indeed enchanted, which was nothing unusual on the Disc, and was also the only forest in the whole universe to be called -- in the local language -- Your Finger You Fool, which was the literal meaning of the word Skund. The reason for this is regrettably all too common. When the first explorers from the warm lands around the Circle Sea travelled into the chilly hinterland they filled in the blank spaces on their maps by grabbing the nearest native, pointing at some distant landmark, speaking very clearly in a loud voice, and writing down whatever the bemused man told them. Thus were immortalised in generations of atlases such geographical oddities as Just A Mountain, I Don't Know, What? and, of course, Your Finger You Fool. Rainclouds clustered around the bald heights of Mt. Oolskunrahod ('Who is this Fool who does Not Know what a Mountain is') and the Luggage settled itself more comfortably under a dripping tree, which tried unsuccessfully to strike up a conversation. Terry Pratchett, The Light Fantastic Specifically, prior to the introduction of -- more about that later.  ↩ That's something I find myself constantly forgetting; I'll talk about "the loss landscape" as if it's something our training loop is exploring. And, of course, there is an overall loss landscape across all of the training data as a whole, but in any given iteration through the training loop, the loss is relative to the specific batch we're looking at.  ↩ You can also pass in an argument, zero by default, to tell it to do the derivative with respect to a different parameter or with respect to a sequence of parameter indexes. If you give a sequence, it will return a tuple of gradients. Additionally, there's a that returns a tuple of the value of and the gradients, which is useful for tracking loss as you train -- we'll use that later on.  ↩ You can also make classes "PyTree-compatible" by providing helper functions that map to and from that representation.  ↩ A reminder if your memory of Python decorator syntax is rusty -- this: ...is just syntactic sugar for this: It's a tad more complicated than that -- the metadata for array traces also contains the shape. More about that later.  ↩ For the pedantic: over ten runs of each, the numbers were pretty stable.  ↩ In case you're thinking that JAX is backed by Google and guaranteed to thrive because of that, remember Ada . Backed by the US Department of Defense. For its time, well-designed and elegant. It's still used, but it's hardly mainstream... I remember reading about it in Byte magazine back in 1988 or so, and had an "it's so beautiful" moment then too. To be fair to me, I was 14.  ↩ PyTorch is engineering; JAX is maths. PyTorch has historically 1 been optimised piecewise, JAX is JITted. PyTorch is procedural, JAX (tries to be) functional. PyTorch is maximalist; JAX is minimalist. Zero out the gradients that you currently have attached to the parameters. Do a forward pass to get the model's outputs. Work out the loss based on those outputs. Do the backward pass. Update the parameters based on the gradients that the backward pass attached to them. They don't know that the MaxSim kernel exists, so their code remains unoptimised. They do know that it exists, so they repurpose it for whatever their use case is. The first time through, it will create another of those tracer objects; this time, though, it won't wrap the number -- it will just know that it is a wrapper for a float. It will call the Python code with that tracer, and all of the operations in the function will be run, but the result that comes out at the end will essentially just be a representation of what calculations were done in an abstract sense -- like the computation graph that was used for working out gradients, but without specific numbers in it. JAX has a nice way to display these representations as what it calls JAXPRs, and the JAXPR for that function's representation when called with a float parameter will look something like this: That JAXPR can be compiled into the appropriate code for the platform where you're running it -- x86 machine code, compiled CUDA, the equivalent for AMD or Google Tensor Processing Units (TPUs), and will be cached. The key for the cache will be meta-information about the parameter -- in this case, something like "a 32-bit floating-point scalar". Next, the compiled code -- not the original Python -- is run with the actual value of the parameter, the that we provided. Horrible hacks can inexplicably become popular, but normally die off when people get tired of swearing at them. (Though sometimes a large installed base means that they linger.) Things of beauty get people excited, and often pull in the best engineers. But eventually, they drop by the wayside. Perhaps there's some hidden flaw that no-one noticed at the outset, or perhaps the mental model you need to build in order to use them effectively is too complicated for them to get to critical mass. Solid, boring engineering wins in the long term. Specifically, prior to the introduction of -- more about that later.  ↩ That's something I find myself constantly forgetting; I'll talk about "the loss landscape" as if it's something our training loop is exploring. And, of course, there is an overall loss landscape across all of the training data as a whole, but in any given iteration through the training loop, the loss is relative to the specific batch we're looking at.  ↩ You can also pass in an argument, zero by default, to tell it to do the derivative with respect to a different parameter or with respect to a sequence of parameter indexes. If you give a sequence, it will return a tuple of gradients. Additionally, there's a that returns a tuple of the value of and the gradients, which is useful for tracking loss as you train -- we'll use that later on.  ↩ You can also make classes "PyTree-compatible" by providing helper functions that map to and from that representation.  ↩ A reminder if your memory of Python decorator syntax is rusty -- this: ...is just syntactic sugar for this: ↩ It's a tad more complicated than that -- the metadata for array traces also contains the shape. More about that later.  ↩ For the pedantic: over ten runs of each, the numbers were pretty stable.  ↩ In case you're thinking that JAX is backed by Google and guaranteed to thrive because of that, remember Ada . Backed by the US Department of Defense. For its time, well-designed and elegant. It's still used, but it's hardly mainstream... I remember reading about it in Byte magazine back in 1988 or so, and had an "it's so beautiful" moment then too. To be fair to me, I was 14.  ↩

0 views
JSLegendDev 2 days ago

My Biggest Gripe With YouTube

3 years ago, I started a YouTube channel called JSLegendDev where I uploaded tutorials teaching the JavaScript programming language through the development of 2D games. The state of the space around the time I started was as follows : Tutorials inferior to an hour in length were not in demand. They made very little views. Tutorials divided into multiple parts where dead on arrival. You were guaranteed dwindling views on every new upload. To adapt, other content creators started uploading longer, multi-hour, often project based tutorials which translated to more views. Seeing the shift, I also decided to follow suit and uploaded tutorials reaching the 4-10 hour mark. I saw some success doing this. Therefore, I kept at it for a while. However, as time passed, I got tired of recording extremely long tutorials and they, in general, started to make less views. There are many hypotheses as to why YouTube’s algorithm started serving tutorial content less. The advent of AI could’ve been the likely cause but also a general shift in YouTube becoming more of an entertainment focused platform to the detriment of educational content. Something you now put on TV to relax. In the programming space, channel producing content that can be watched passively like tech news, tech drama, tech history, high level discussions, etc… continued to thrive. Seeing this new shift and because I was genuinely tired of making YouTube tutorials, I published my first scripted video titled “How do Devs Make Levels Without Game Engines” which was first published as an article. In that piece, I told the story of how I discovered a convenient way to design levels for my games using an external editor called Tiled in conjunction with my editor-less game framework. At the end of that video, I promoted a paid tutorial I made teaching the exact steps needed to achieve what was presented. The video ended up accumulating over 30k views, which was pretty great! It took far less effort to make compared to my multi-hour tutorials and I was able to make a few sales on my paid tutorial I mentioned within. Previously, I was very unsuccessful in selling any paid courses and I didn’t quite understand why. However, the answer now hit me like a truck. Why would anyone still have the appetite for a paid course after having invested the time following a free multi-hour course? Even if the subject of the paid offering was different, they would probably be too tired to commit to another one. Anyway, following in the footsteps of this first breakthrough, I uploaded another scripted video titled “You Can Now Make PS2 Games in JavaScript” which was again first published as an article. In that video, I told the story of how I discovered that you could make PS2 games in JavaScript and provided an overview of how the viewer could get started. Despite including very practical knowledge, the viewer was never expected to follow along and therefore could watch it passively. It was a resounding success, over 100k views! Unfortunately, I didn’t sell any courses in that video because I simply didn’t have the energy to both make the video and a course. The best business decision would have been to wait before uploading. I’ll go into more details later, but my biggest gripe with YouTube is that it’s no longer a great platform to build an audience but rather it’s only good for reach and here, I had wasted a lot of reach. After having made so many game development tutorials, I wanted to try my hand in creating an original game that I would sell on Steam. Once the project was starting to take shape, I had the idea of making a video about it to gauge interest as I wasn’t sure it would find an audience. Therefore, I had the idea of using the same format used in my two previous successful videos. However, rather than focusing on technical details, I instead would tell the story of how I came up with my game’s design covering the various iterations and challenges I faced while working on it. Therefore, I ended up uploading a video titled “Making a Small RPG” which again, was originally an article. It was also a resounding success reaching barely below 100k views! However, it came with a hidden cost. That cost was the tipping point that made me realize that YouTube is no longer a good platform to build an audience on. I naively thought that if the video performed well, this would translate to subscribers and an audience eager to hear more about the project, but this wasn’t the case. I had made a big mistake by not setting up a Steam page to direct viewers to before publishing the video. On my next upload concerning the project, the fall off in terms of views was brutal. I went from 98k views to below 10k. It became clear that YouTube was acting as a gatekeeper between me and the audience I thought I had built. After reflecting on the situation, I came to the following conclusion. The reason my 3 previous videos had performed well was due having certain characteristics that aligned with YouTube’s goal as a platform, which consists in making people watch videos for as long as possible so they can serve more ads. I listed them below : The subject of all three videos were remarkable which lead to people clicking on them. Something is remarkable when it obviously stands out as being interesting/noteworthy. For example, the subject of my video titled “You Can Now Make PS2 Games in JavaScript” is remarkable because the PS2 is a very popular, but now old console and you had to use a hard programming language called C++ to make games for it. Being able to now use JavaScript, a simpler but most importantly, a language originally designed for making websites and not games, makes the subject come across as immediately noteworthy. Therefore, remarkable. The use of storytelling made people eager to watch more of the video. This can be explained by the fact that we instinctively want to know what happens next in a compelling story. Finally, the length of the videos were all above 10 minutes and the 2 more successful ones were in the 15+ min range. This resulted in more absolute watch time compared to shorter content. For example, if 2 videos are both watched fully by the same audience. The shorter one will translate to less total time spent on the platform compared to the longer one. Therefore, YouTube will recommend the longer one instead because there’s an opportunity cost to doing otherwise. To understand the fall off, it’s important to first mention that usually, series on YouTube don’t work. The second video of a series ends up making less views than the first because it requires prior context before clicking. Thus reducing its appeal and limiting its reach. However, I knew this going in. I tried making the second video as independent as possible but in the end, a second video talking about the same subject was bound to be less remarkable. It didn’t help that because I summarized the content of the first video in the second one, a familiar viewer would have found it less engaging making the video further away from hitting criteria 2 and 3 that I outlined above. Consequently, I realized I had wasted my biggest marketing ammunition regarding my small RPG game as I had no way to contact the audience hit by the first video. Like with the one on making PS2 games in JavaScript, I had wasted tremendous reach. At this point, I realized my biggest gripe with YouTube was simply that I could not access my audience reliably. Therefore, was it really my audience? On one hand, YouTube allows someone without a following to reach millions but on the other, the link to those reached is fickle. I thought I was building an audience by gaining subscribers but instead, I was building a sand castle that could easily be carried away by the slightest algorithm waves. YouTube wasn’t always like this. People used to subscribe to channels and seek their content in their subscriptions tab. However, the platform effectively buried this model by conditioning users to seek recommended videos on the home page and deprioritizing the Subscriptions tab to the point that it barely looks like a clickable section. You have to click on the “Subscriptions” text to access your sub feed. Doesn’t look very clickable doesn’t it? I think that we’re now entering an era where YouTube is starting to treat content creators as interchangeable much like TikTok. They saw the success TikTok had, tried to replicate it with Shorts and now YouTube long form is getting affected as well. I fear that in the future, uploading to YouTube will look no different than making posts on Reddit. You might get views, you might get comments, but they’re self contained to a specific post with no following building up and no guarantee of your next posts having the same reach. The conclusion to all of this is that it’s not worth it to be a YouTuber. Relying on YouTube adsense and sponsorships (sponsors use views as a metric to determine how much to pay you) for your livelihood is simply not sustainable due to how fickle getting views on the platform is. Therefore, focusing so much on making YouTube content will most likely lead to your exploitation. That said, is quitting really the answer? Considering that YouTube can give you incredible reach even if you’re a nobody as long as you make content that is remarkable, engaging (for example, through storytelling) and long enough, it would be stupid to completely walk away, at least in my case. Therefore a new strategy appears on the horizon. It consists in building your audience outside of YouTube through a mailling list (Substack conveniently allows you to do so) and to strategically make occasional compelling YouTube content to tap into the platform’s reach potential. However, the key is to always direct viewers to the mailling list. Why is building an audience through email so important? because it allows you to have a direct and long lasting link with your audience. It also gives you independence from social media platforms. Even in the case of Substack, where this article is currently hosted, I can export my email list and move to another platform or email sending service without my subscribers even noticing. This shift implies that I no longer need to worry about pumping frequent content for YouTube because I’m not making money through them or worrying about doing so. By making YouTube content rarely, I get to keep most of my energy to build something compelling outside the platform like an actual game, writing interesting articles, making an in-depth course or other kinds of art/products. This plan seems to me as more sustainable and more healthy long term. That’s about all I’ve got to share. Hope this article was insightful. If you’re curious to see where this journey will lead, I recommend subscribing! I usually write about programming, game development and game design. Subscribe now You can check some of my previous articles below. Tutorials inferior to an hour in length were not in demand. They made very little views. Tutorials divided into multiple parts where dead on arrival. You were guaranteed dwindling views on every new upload. The video ended up accumulating over 30k views, which was pretty great! It took far less effort to make compared to my multi-hour tutorials and I was able to make a few sales on my paid tutorial I mentioned within. Previously, I was very unsuccessful in selling any paid courses and I didn’t quite understand why. However, the answer now hit me like a truck. Why would anyone still have the appetite for a paid course after having invested the time following a free multi-hour course? Even if the subject of the paid offering was different, they would probably be too tired to commit to another one. Anyway, following in the footsteps of this first breakthrough, I uploaded another scripted video titled “You Can Now Make PS2 Games in JavaScript” which was again first published as an article. In that video, I told the story of how I discovered that you could make PS2 games in JavaScript and provided an overview of how the viewer could get started. Despite including very practical knowledge, the viewer was never expected to follow along and therefore could watch it passively. It was a resounding success, over 100k views! Unfortunately, I didn’t sell any courses in that video because I simply didn’t have the energy to both make the video and a course. The best business decision would have been to wait before uploading. I’ll go into more details later, but my biggest gripe with YouTube is that it’s no longer a great platform to build an audience but rather it’s only good for reach and here, I had wasted a lot of reach. After having made so many game development tutorials, I wanted to try my hand in creating an original game that I would sell on Steam. Once the project was starting to take shape, I had the idea of making a video about it to gauge interest as I wasn’t sure it would find an audience. Therefore, I had the idea of using the same format used in my two previous successful videos. However, rather than focusing on technical details, I instead would tell the story of how I came up with my game’s design covering the various iterations and challenges I faced while working on it. Therefore, I ended up uploading a video titled “Making a Small RPG” which again, was originally an article. It was also a resounding success reaching barely below 100k views! However, it came with a hidden cost. That cost was the tipping point that made me realize that YouTube is no longer a good platform to build an audience on. I naively thought that if the video performed well, this would translate to subscribers and an audience eager to hear more about the project, but this wasn’t the case. I had made a big mistake by not setting up a Steam page to direct viewers to before publishing the video. On my next upload concerning the project, the fall off in terms of views was brutal. I went from 98k views to below 10k. It became clear that YouTube was acting as a gatekeeper between me and the audience I thought I had built. After reflecting on the situation, I came to the following conclusion. The reason my 3 previous videos had performed well was due having certain characteristics that aligned with YouTube’s goal as a platform, which consists in making people watch videos for as long as possible so they can serve more ads. I listed them below : The subject of all three videos were remarkable which lead to people clicking on them. Something is remarkable when it obviously stands out as being interesting/noteworthy. For example, the subject of my video titled “You Can Now Make PS2 Games in JavaScript” is remarkable because the PS2 is a very popular, but now old console and you had to use a hard programming language called C++ to make games for it. Being able to now use JavaScript, a simpler but most importantly, a language originally designed for making websites and not games, makes the subject come across as immediately noteworthy. Therefore, remarkable. The use of storytelling made people eager to watch more of the video. This can be explained by the fact that we instinctively want to know what happens next in a compelling story. Finally, the length of the videos were all above 10 minutes and the 2 more successful ones were in the 15+ min range. This resulted in more absolute watch time compared to shorter content. For example, if 2 videos are both watched fully by the same audience. The shorter one will translate to less total time spent on the platform compared to the longer one. Therefore, YouTube will recommend the longer one instead because there’s an opportunity cost to doing otherwise.

0 views
ava's blog 2 days ago

what i read this week - week 22 2026

Thought that after my post on summary distrust, I could share a list of what I read each week. I technically prefer to process and digest what I read into blog posts, but not everything makes it into one, and this is a way to document and keep them, and maybe give others some food for thought. This is not necessarily stuff I fully agree with, I'm just sharing what ended up in my feed reader or was linked in stuff I read, and doesn't include all the personal blog posts I read. I had a lot to catch up on because I read a lot less the previous 2 weeks. AI detection was built for faces - article about how bad AI detection works for war and climate propaganda videos, as the detection mechanisms often rely on biometric human features, and cannot accurately detect fake fire, smoke effects, etc. US Law Enforcement Warns of ‘Anti-Tech Extremism’ - the US gov is aware of the sentiment around AI and is willing to target and suppress it, and they have little paid accomplice firms too who keep surveilling you on social media and in real life meetings if you organize to oppose data centers or voice criticism about them. Iran Israel AI war propaganda - The AI propaganda we see with armed conflicts right now is a dire warning to the future of online video information. Goes more in-depth about detection methods. Can Tracking Private Jets Predict an Imminent Apocalypse? - article about a site that assumes the rich elites will find out about an apocalypse first and try to flee, therefore serving as a warning system to the rest of us. Why GCC Nations Must Move Beyond Content Moderation to Regulate Harm by Design - GCC means Governments in the Gulf Cooperation Council. Article is about how certain countries have already heavily regulated (and, arguably, censored in their favor) social media platform content, so now they should do the same for platform design. Eh... Big Tech Will Not Save Us From the Climate Crisis - Big Tech is moving away from their climate targets and carbon credit bullshit because they wanna do more AI and data centers. The rest they are doing is unproven or not working. Definition of Overburdened Communities in New Jersey - data centers and other similar detrimental undertakings often target overburdened communities, and this is what it means. A Town Hall Too Late - article documenting how citizens near an almost finished data center actually get informed and treated (not well). They only received information well after the thing started to get built. It is being developed by DataOne for the Nebius Group to support AI infrastructure as part of a $17 billion deal with Microsoft. Meta loses High Court challenge - summary of the case and possible fine. Responsible Innovation Harms Modeling on Microsoft's Learning Platform. EU AI Omnibus Deal Changes - more analysis on the proposed AI Act changes, nudifier ban and more, prominent actors, Merz ruining everything for us as usual, etc. The AI Act is not ready for agents - article for a paper that's also listed below; risks of agents, and a need for more guidance from the AI Office. AI’s real threat is worker control and surveillance - about the divide between workers who use AI and those who are managed by it. Higher paid jobs can be supplemented and accelerated by it, while the less fortunate, less earning (warehouse, gig work) are suffering under AI micromanaging them, causing scheduling issues, errors and more, and are more intensely surveilled than ever by AI "bossware". Entzauberung der Digitalen Souveränität - German; deconstructing the term "digital sovereignty" and ideas around it. Mostly about this talk. AI Forensics gegen Big Tech - German; Interview with AI Forensics founder Marc Faddoul about his work and the fear of retribution, especially the fear about getting targeted by Elon Musk. Human Rights Due Diligence - info on what downstream HRDD is. Microsoft took a step towards human rights - very charitable and exaggerated read of Microsoft parting ways with their Israel chief and their ties to the Israeli Ministry of Defense, plus suspending some of their services. The World Is Already Resisting AI - Article on the AI Resist List , a collaboratively built, publicly accessible database documenting acts of resistance to the AI industry from across the world. AI Data Centers: Big Tech's Impact on Electric Bills, Water, and More - looking at different papers and studies around the water and electricity use of big data centers, where they are located, and what local problems they are worsening. Meta’s Hyperion project in Louisiana will need three times as much electricity as the entire city of New Orleans, and is bigger than its main airport. They also gag local officials with NDA's so they can't properly inform the residents. What you need to know about data centers - information on what Earthjustice attorneys are doing to push for stronger environmental protections targeting data centers. The Web Is Being Made Accessible for AI, Not People - llms.txt convention, MCP etc.; companies are more ready to make their services accessible to AI agents than disabled people. This shouldn't be seen as another curb cut phenomenon. Bitte im Omnibus sitzen bleiben, liebe PIMS - German article about the Art. 88 reworks for Personal Information Management Systems that are supposed to enable an easier handling of cookie consent and tracking. Social Media Verbot weder wissenschaftlich fundiert noch effektiv - German; about how there is no scientific proof that social media bans will help, and some stats about how many people support social media bans, and for what age group. Big Tech und Staat - German article on how the state seems to increasingly serve private interests, especially Big Tech. Bundesregierung will KI Einsatz der Polizei - German article about use of AI software for law enforcement, its risks, and what rights are threatened. Polizeigesetznovelle Schleswig-Holstein - German article discussing Schleswig-Holsteins attempt at changing their police law, including real-time facial recognition, behavioral surveillance, online face search and more, from strangers on the street, and even mere victims or witnesses of crimes. Das Internet verrottet - German; about link rot and archiving things properly. Why “Made in Europe” Won’t Fix AI’s Deeper Problems - fitting to my blog post. Big Tech as Executor of the dead - was also a topic at the conference. Praxisfolgen Russmedia Urteil - consequences for social media platforms following the Russmedia court decision C-492/23; Notice-And-Sweep. AI Act: deal on simplification measures, ban on “nudifier” apps - concluding what deal was reached between co-legislators; names the new deadlines for AI compliance. Ratepayer Protection Pledge by the White House - promises and propaganda Microslop's Community-First AI Infrastructure Pledge - promises and propaganda vol. 2 Anthropic's Promises - promises and propaganda vol. 3 Offener Brief der Industrie - Open letter to German politicians by German industry criticizing parts of the digital omnibus; it was silly to read, and I think it is disrespectful to imply that technologies can be discriminated against; that's a different usage and connotation than just using it as "being discriminated from" (aka being differentiated from others). None of the arguments are convincing. Draft guidelines for the implementation of transparency obligations for certain AI systems under Art. 50 AI Act - this is out for commenting until the 3rd of June, by the way. Consent Fatigue entgegenwirken - German policy brief by the TUM think tank about countering consent fatigue. Data Center Fight Guide Einstellungen zum geplanten Einsatz von Palantir-Software II - German phone survey about Palantir use by Verian & campact from Sep 2025. Grok Unleashed - Analyzing Grok nudify uses and extremist propaganda, by AI Forensics. Distinguishing Authentic from AI-Generated Explosions using Spatiotemporal Dynamics - more about how to authenticate conflict-zone explosion footage. AI footage tends to produce much bigger, rounder mushroom plumes that expand quicker. Don't ask me about the math, I don't understand any of that, but I found the rest I could understand very interesting. Embedding Human Rights in Technical Standards - About WITNESS' experience in the Coalition for Content Provenance and Authenticity (C2PA), which is in favor of open technical standards to embed verifiable provenance metadata into digital media files. Helpful explainer here . Better Images of AI - a guide for creators and users on how to use accurate images when talking about AI and what to avoid, as it shapes the narrative. Specifically, they call to avoid the color blue, descending code, human brains, science fiction elements, white robots, anthromorphism and references to the Creation of Adam. That is because it misrepresents capabilities, risks and fears, and who is or can work in or with AI (often, only white men are shown). The AI Climate Hoax : Behind the Curtain of How Big Tech Greenwashes Impacts - talks about how different kinds of AI and its uses as well as carbon credits and overstating the climate benefits of AI can be used to hide the environmental impact of the big, hyped up GenAI. Big Tech’s ‘False Solutions’ to the Climate Crisis - similar thing here. Debunking nuclear power, carbon capture, and artificial intelligence as helping climate change. There are endnotes at each chapter, so don't miss what's after. Tackling Arbitrary Digital Surveillance in the Americas - uses Cajar vs. Colombia for some examples to showcase what needs to change, and the importance of the three-step-analysis. Basically all of this is standard here in the EU, but still needs to be implemented there. TRIED AI Detection Benchmark - paper from WITNESS about their framework that evaluates AI detection tools through a sociotechnical lens (with a focus on adaptability, transparency, accessibility, contextual relevance, and fairness). Wasn't a complete fan, because a chunk of it (for example about resource investments) is rather vague, theoretical and hardly connected with a direct or objective way to measure in practice. The rest is mostly fair, but also rather obvious, and some of it is basically impossible to combine in practice - like only using datasets that comply with data protection and intellectual property laws and are "ethical" with no sensitive data, while the models are supposed to reliably detect an AI generated video of a minority language or niche culture, or have enough datasets (= lots) to accurately detect cultural and local contexts. I can't quite pinpoint what exactly bothers me about it otherwise. I did like the examples of real use cases where things failed. In total, that is roughly ~ 340 pages, if we count an article as two pages on average. Most of it was read on Sunday and Monday (holiday), as I had a lot of free time then. Reply via email Published 30 May, 2026 AI detection was built for faces - article about how bad AI detection works for war and climate propaganda videos, as the detection mechanisms often rely on biometric human features, and cannot accurately detect fake fire, smoke effects, etc. US Law Enforcement Warns of ‘Anti-Tech Extremism’ - the US gov is aware of the sentiment around AI and is willing to target and suppress it, and they have little paid accomplice firms too who keep surveilling you on social media and in real life meetings if you organize to oppose data centers or voice criticism about them. Iran Israel AI war propaganda - The AI propaganda we see with armed conflicts right now is a dire warning to the future of online video information. Goes more in-depth about detection methods. Can Tracking Private Jets Predict an Imminent Apocalypse? - article about a site that assumes the rich elites will find out about an apocalypse first and try to flee, therefore serving as a warning system to the rest of us. Why GCC Nations Must Move Beyond Content Moderation to Regulate Harm by Design - GCC means Governments in the Gulf Cooperation Council. Article is about how certain countries have already heavily regulated (and, arguably, censored in their favor) social media platform content, so now they should do the same for platform design. Eh... Big Tech Will Not Save Us From the Climate Crisis - Big Tech is moving away from their climate targets and carbon credit bullshit because they wanna do more AI and data centers. The rest they are doing is unproven or not working. Definition of Overburdened Communities in New Jersey - data centers and other similar detrimental undertakings often target overburdened communities, and this is what it means. A Town Hall Too Late - article documenting how citizens near an almost finished data center actually get informed and treated (not well). They only received information well after the thing started to get built. It is being developed by DataOne for the Nebius Group to support AI infrastructure as part of a $17 billion deal with Microsoft. Meta loses High Court challenge - summary of the case and possible fine. Responsible Innovation Harms Modeling on Microsoft's Learning Platform. EU AI Omnibus Deal Changes - more analysis on the proposed AI Act changes, nudifier ban and more, prominent actors, Merz ruining everything for us as usual, etc. The AI Act is not ready for agents - article for a paper that's also listed below; risks of agents, and a need for more guidance from the AI Office. AI’s real threat is worker control and surveillance - about the divide between workers who use AI and those who are managed by it. Higher paid jobs can be supplemented and accelerated by it, while the less fortunate, less earning (warehouse, gig work) are suffering under AI micromanaging them, causing scheduling issues, errors and more, and are more intensely surveilled than ever by AI "bossware". Entzauberung der Digitalen Souveränität - German; deconstructing the term "digital sovereignty" and ideas around it. Mostly about this talk. AI Forensics gegen Big Tech - German; Interview with AI Forensics founder Marc Faddoul about his work and the fear of retribution, especially the fear about getting targeted by Elon Musk. Human Rights Due Diligence - info on what downstream HRDD is. Microsoft took a step towards human rights - very charitable and exaggerated read of Microsoft parting ways with their Israel chief and their ties to the Israeli Ministry of Defense, plus suspending some of their services. The World Is Already Resisting AI - Article on the AI Resist List , a collaboratively built, publicly accessible database documenting acts of resistance to the AI industry from across the world. AI Data Centers: Big Tech's Impact on Electric Bills, Water, and More - looking at different papers and studies around the water and electricity use of big data centers, where they are located, and what local problems they are worsening. Meta’s Hyperion project in Louisiana will need three times as much electricity as the entire city of New Orleans, and is bigger than its main airport. They also gag local officials with NDA's so they can't properly inform the residents. What you need to know about data centers - information on what Earthjustice attorneys are doing to push for stronger environmental protections targeting data centers. The Web Is Being Made Accessible for AI, Not People - llms.txt convention, MCP etc.; companies are more ready to make their services accessible to AI agents than disabled people. This shouldn't be seen as another curb cut phenomenon. Bitte im Omnibus sitzen bleiben, liebe PIMS - German article about the Art. 88 reworks for Personal Information Management Systems that are supposed to enable an easier handling of cookie consent and tracking. Social Media Verbot weder wissenschaftlich fundiert noch effektiv - German; about how there is no scientific proof that social media bans will help, and some stats about how many people support social media bans, and for what age group. Big Tech und Staat - German article on how the state seems to increasingly serve private interests, especially Big Tech. Bundesregierung will KI Einsatz der Polizei - German article about use of AI software for law enforcement, its risks, and what rights are threatened. Polizeigesetznovelle Schleswig-Holstein - German article discussing Schleswig-Holsteins attempt at changing their police law, including real-time facial recognition, behavioral surveillance, online face search and more, from strangers on the street, and even mere victims or witnesses of crimes. Das Internet verrottet - German; about link rot and archiving things properly. Why “Made in Europe” Won’t Fix AI’s Deeper Problems - fitting to my blog post. Big Tech as Executor of the dead - was also a topic at the conference. Praxisfolgen Russmedia Urteil - consequences for social media platforms following the Russmedia court decision C-492/23; Notice-And-Sweep. AI Act: deal on simplification measures, ban on “nudifier” apps - concluding what deal was reached between co-legislators; names the new deadlines for AI compliance. Ratepayer Protection Pledge by the White House - promises and propaganda Microslop's Community-First AI Infrastructure Pledge - promises and propaganda vol. 2 Anthropic's Promises - promises and propaganda vol. 3 Offener Brief der Industrie - Open letter to German politicians by German industry criticizing parts of the digital omnibus; it was silly to read, and I think it is disrespectful to imply that technologies can be discriminated against; that's a different usage and connotation than just using it as "being discriminated from" (aka being differentiated from others). None of the arguments are convincing. Draft guidelines for the implementation of transparency obligations for certain AI systems under Art. 50 AI Act - this is out for commenting until the 3rd of June, by the way. Consent Fatigue entgegenwirken - German policy brief by the TUM think tank about countering consent fatigue. Data Center Fight Guide Einstellungen zum geplanten Einsatz von Palantir-Software II - German phone survey about Palantir use by Verian & campact from Sep 2025. Grok Unleashed - Analyzing Grok nudify uses and extremist propaganda, by AI Forensics. Distinguishing Authentic from AI-Generated Explosions using Spatiotemporal Dynamics - more about how to authenticate conflict-zone explosion footage. AI footage tends to produce much bigger, rounder mushroom plumes that expand quicker. Don't ask me about the math, I don't understand any of that, but I found the rest I could understand very interesting. Embedding Human Rights in Technical Standards - About WITNESS' experience in the Coalition for Content Provenance and Authenticity (C2PA), which is in favor of open technical standards to embed verifiable provenance metadata into digital media files. Helpful explainer here . Better Images of AI - a guide for creators and users on how to use accurate images when talking about AI and what to avoid, as it shapes the narrative. Specifically, they call to avoid the color blue, descending code, human brains, science fiction elements, white robots, anthromorphism and references to the Creation of Adam. That is because it misrepresents capabilities, risks and fears, and who is or can work in or with AI (often, only white men are shown). The AI Climate Hoax : Behind the Curtain of How Big Tech Greenwashes Impacts - talks about how different kinds of AI and its uses as well as carbon credits and overstating the climate benefits of AI can be used to hide the environmental impact of the big, hyped up GenAI. Big Tech’s ‘False Solutions’ to the Climate Crisis - similar thing here. Debunking nuclear power, carbon capture, and artificial intelligence as helping climate change. There are endnotes at each chapter, so don't miss what's after. Tackling Arbitrary Digital Surveillance in the Americas - uses Cajar vs. Colombia for some examples to showcase what needs to change, and the importance of the three-step-analysis. Basically all of this is standard here in the EU, but still needs to be implemented there. TRIED AI Detection Benchmark - paper from WITNESS about their framework that evaluates AI detection tools through a sociotechnical lens (with a focus on adaptability, transparency, accessibility, contextual relevance, and fairness). Wasn't a complete fan, because a chunk of it (for example about resource investments) is rather vague, theoretical and hardly connected with a direct or objective way to measure in practice. The rest is mostly fair, but also rather obvious, and some of it is basically impossible to combine in practice - like only using datasets that comply with data protection and intellectual property laws and are "ethical" with no sensitive data, while the models are supposed to reliably detect an AI generated video of a minority language or niche culture, or have enough datasets (= lots) to accurately detect cultural and local contexts. I can't quite pinpoint what exactly bothers me about it otherwise. I did like the examples of real use cases where things failed. Zugänglichkeit von De-Personalisierungsoptionen und Meldeverfahren auf sehr großen Online-Plattformen Decisions I had to read to translate for noyb: 2025-0.875.804 and W171 2305420-1

0 views
Unsung 2 days ago

“The broken, slapdash, bed-shitting end to one of the most iconic franchises in all of gaming history”

I absolutely love Billy Maher’s body of work . He’s been writing about older games and software in general since 2011; it’s always solid, always an enjoyable read, and always providing new perspectives even on stuff I thought I knew well. (Maher also goes by The Digital Antiquarian.) I linked to his work once before , and today I wanted to share a recent essay about the disaster that was the 1999’s game Ultima IX . = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-broken-slapdash-bed-shitting-end-to-one-of-the-most-iconic-franchises-in-all-of-gaming-history/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/the-broken-slapdash-bed-shitting-end-to-one-of-the-most-iconic-franchises-in-all-of-gaming-history/1.1600w.avif" type="image/avif"> I have never played any Ultima games, but this felt a gripping read. […] Richard Garriott, the motivating force behind Ultima from first to last, has done his level best to write the aforementioned last out of history entirely. Ultima IX is literally never mentioned at all in his autobiography. But, much though I may be tempted to, I can’t similarly sweep under the rug the eminently unsatisfactory denouement to the Ultima series. I have to tell you how this unfortunate last gasp fits into the broader picture of the series’s life and times, and do what I can to explain to you how it turned out so darn awful. In some sense software projects always fail for one of the few obvious reasons, and it’s just details that change. Here, the details are fascinating. The Ultima series started in the very early 1980s as a series of small games made by one person, and ended ignominiously as an almost-AAA title rushed to market that no longer wanted it: They met the deadline — what other choice did they have? — but the playable game eluded them. It’s not just the deadline. There’s also a studio past its prime, a fascinating but deeply flawed leader, the market forces and trends, and perhaps even some enshittification long before the word’s invention. It is also a story of the first two decades of the videogame industry itself. It happened so long ago that it almost feels like a fairytale itself, but one with a sad ending. Maher also lists some learnings that are universal enough to apply to a lot of other projects: And, in case you want more, here are handy links to Maher’s all Ultima essays: I (3 parts!), II (3 parts), III , IV , Multima , V , VI , Worlds , Underworld (2 parts), VII , and VIII . I have no personally read them in order, and I’m better for it. #enshittification #games #software evolution No game can be all things to all people. Development teams need a clear leader with a clear vision. Checking off a list of bullet points sent down from marketing does not a good game make. When the design goals do change radically, it’s often better to throw everything out and start over from scratch than to keep retro-fitting bits and pieces onto the Frankenstein’s monster. It’s better to release a good game late than a bad game on time.

0 views
Brain Baking 2 days ago

Lawyer (Should Have Been a Marine Biologist)

In Tattoorist , a 2026 Flemish TV show, musician and tattoo artist Tijs Vanneste explores six European capitals through the lens of the tattoo world. In the first episode , he meets up with a famous artist from London whilst exploring city’s more sketchy corners (literally and figuratively) with local guides. During the episode, we briefly see Tijs exploring the Highgate East Cemetery where he visits the graves of various famous people and reflects on his own life choices. There was a specific gravestone that caught my eye. The following words are engraved onto it: Lawyer. Should Have Been a Marine Biologist. 1958 - 2015. Lawyer. Should Have Been a Marine Biologist. The short take doesn’t show the entire engraving. Tijs remarks that the engravings in the cemetery are much sharper, funnier, and wittier than a typical cemetary in Belgian that would just state dates and perhaps at most something like “beloved family”. He then moves on to go do other tattooy stuff. Weeks after watching the episode, I still had Should Have Been a Marine Biologist lingering inside my mind, like an unwelcome visitor that I repeatedly fail to show out. Why is this striking example of an enigmatic epitaph so important for me? The admittance of making a wrong career choice written forever on your gravestone is not just funny but can also be interpreted as a friendly warning to do with your life what you want to do instead of what others want you to do. Thanks to a The London Dead blog post on the Highgate East Cemetery (what an amazing blog, by the way), I learned that this specific grave belongs to Sally Hunter. Film director Gurinder Chadha, one of her best friends, explains the origin behind the quip: Sally was one of my best friends, she fell into Law at university but never wanted to be a lawyer. Her big passion was snorkelling, diving and the sea. she would escape to Egypt, or anywhere she could snorkel, at a drop of a hat. She was a very well read, witty, passionate person who made the wrong choice in career life. We miss her terribly. The wrong career choice in life. I guess she must have failed Nietzsche’s Will To Live experiment: would you want to endlessly repeat the live you’re living now or want to make some much-needed changes the next time you’re living it? I wonder if Sally was the one who chose the epitaph and wrote it on her will. Probably not. What would your own epitaph say? Mine’s quite easy: Dr. Wouter Groeneveld. Brain Baker. And of course something along the lines of “loving husband & father of …”. Hopefully, as I’m no longer able to make that decision. Hopefully, by then, I am still loved. Who knows what your relatives will make of it. One thing is for sure: you won’t be the one who’ll get to see it. Unless you plan on returning as a ghost, of course. Most gravestones in the area don’t contain any special epitaph at all: the only text on it is the name, the data of birth and death, and optional close relatives such as significant other and/or children. Inspired by the London Dead blog, I plan on compiling a Hasselt Dead post in the future to highlight the exceptions. The last funerals I attended did not involve any gravestones at all: more and more, people opt for cremation instead, significantly reducing the available size on the plaque—if any. In our city, there are strict rules concerning the placement of monuments/urns/plaques that besides the obvious dimension rules also dictate the colour and way the letters are to be placed. Nothing on text beyond names and dates though. Name plaques for cremations are kept for 20 years and then removed. The only thing I could find local police regulation rule number 120 (my translation): In cemeteries, the appliance of any posters, advertisements, inscriptions, and objects not provided for in the Law on Cemeteries and Funeral Services is prohibited. In cemeteries, inscriptions and epitaphs may not be of a nature to disturb propriety, order, and the respect due to the deceased. So no fuck or God is a DJ , I guess? To be continued after I visited all the 21 local cemetaries 1 ! If you can’t wait, there’s findagrave.com that claims our local village’s cemetary contains 112 memorials , although most grave photos are missing.  ↩︎ By Wouter Groeneveld on 30 May 2026.  Reply via email . If you can’t wait, there’s findagrave.com that claims our local village’s cemetary contains 112 memorials , although most grave photos are missing.  ↩︎

0 views
Unsung 2 days ago

Shift & ⌥ & Splat & ⎋ Escape

The biggest smallest GUI design schism between Apple’s platforms and Windows isn’t the black vs. white cursor or where to put the menu bar. It’s the presentation of keyboard shortcuts. On a Mac, the shortcuts are iconographic. Command is ⌘. Option is ⌥. Shift is ⇧. Control is ⌃. Fn is 🌐 . There are also icons for all the other non-printing keys, from the relatively well-known Tab (⇥), through the perennially confusable End and PgDn (⤓ and ⇟), to the absolutely cryptic Esc (⎋). On Windows, the keyboard legends are mostly text. PC lost the icon battle in the early 1980s – IBM had them on their 1970s computers, worldwide, but apparently American users of the early IBM PC hated them – and the names are spelled out (Shift and Enter and Home), or close to it (Ctrl, Esc, PgDn, Prt Sc). Why did Apple go this way? My speculation is the revered Braun and generally hi-fi hardware: a lot of stuff sold in Europe defaults to iconography in part because that makes exporting easier. Icons are also more compact – putting ⇧⌘C in a menu or a tooltip takes up a lot less space than Shift+Ctrl+C – and more beautiful when done well. Here’s Figma’s right click menu on Mac and Windows: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/1.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/2.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/2.1600w.avif" type="image/avif"> But there are also challenges, as icons are more cryptic and confusing. “Command” tells you something about itself out of the box, but “⌘” is completely abstract. (Arguably, only arrow keys and symbols like ⇥ and ↵ explain themselves visually.) The attendant issue is that icons are hard to talk about if you don’t know their names, hence tons of jargon like “propeller,” “splat,” or “beanie” for ⌘, for example. It’s a hard situation. Here is one of Mac’s own menus being thoroughly inconsistent, and an example of CleanShot using both the icon and the label to be sure: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/3.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/4.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/4.1600w.avif" type="image/avif"> “Why not both” seems to be the best way in places you can afford it. Apple started doing that on the keyboards too, but it took them decades to get there for modifier keys alone. Even on the 2026 computers, many other keys like Esc and Tab are still single-legended: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/5.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/5.1600w.avif" type="image/avif"> With all that in mind, I want to show you what I saw the other day in Google Docs, on my Mac: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/6.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/shift-and-option-and-splat-and-escape/6.1600w.avif" type="image/avif"> This is one of those cryptic things that I would love to understand the thinking behind. Because, on the surface, this breaks so many rules: There is also a visual argument that cannot be ignored. We’ve been there once before ; if in your menu keyboard shortcuts start overwhelming the commands themselves, you are probably doing something wrong. The only explanation for this I can think of off the top of my head is this: these were invented somewhere else (Word?) and inherited by Docs to respect motor memory of the users transition from the older app. That still doesn’t cover the presentation, plus there is a way for Docs to redesign the shortcuts to be better for people who are starting anew. Ultimately, I think all of this also breaks a cardinal rule: it makes keyboard operation feel more scary and intimidating than it needs to be. Shortcuts are scary enough on their own, and they don’t need any help in this area. #google #iconography #keyboard #mac os A strange hybrid of Mac and Windows styling: some modifier keys are spelled out, and the others are iconographic. (It’s very strange to see ⌘ conjoined with others using a plus!) Complex and generally uncommon dual key shortcuts – to collapse the sidebar, you really need to press ⌃⌘A and then press ⌃⌘H, in sequence. Three-modifier-shortcuts are in general really unpleasant and Google Docs does not seem complicated enough to warrant them. (You can’t see that, but they’re also unreliable! ⌃⌘A ⌃⌘H doesn’t always work and seems to depend on where the focus is.)

0 views
Daniel Mangum 2 days ago

JLink JTAG Access on the Pinecil

It has been more than two years since I bought a Pinecil soldering iron and wrote about soldering the breakout board and accessing the UART. I’ve been doing more work with the Pinecil as of late following the addition of upstream support for the Bouffalo Lab BL706 MCU in Zephyr (big shout out to @VynDragon, @will-tm, @josuah, and everyone else who has been contributing to the upstream Bouffalo Lab efforts!).

0 views
Manuel Moreale 2 days ago

AI blog question challenge

Rishabh emailed me the other day, asking me to answer the 7 questions of his new blog challenge, and who am I to say no to such a request? So here we go. I assume by AI models we mean the current crop of LLMs, and not AI models in general, because I’m old enough to remember when “Machine Learning” was a thing. What even is AI anyway at this point, since everything is lumped together into one useless definition? Anyway, I believe my first experience was trying out chagpt back when it first came out. I don’t think I spent more than 10 or 15 minutes using it at the time. It was impressive tech, but was also completely useless for me at the time, and that’s why I didn’t bother spending more time using it. This is an interesting question. Do I use AI? Well, I guess the answer is yes since it’s almost impossible to avoid using it if you use the web at this point. Pretty much all tools and services are integrating some sort of AI-powered functionalities, and it’s become harder and harder not to use them. If, instead, the question is do I use one of the various LLMs directly to do stuff, then the answer is still yes, but the amount of usage is so low that some people might consider that to be the same as not using them at all. I don’t directly pay for any of the models, but my work email has been powered by Google for more than a decade, and so I do get access to Gemini Pro. Workspace has usage data for everything, and I just looked it up: In the last 90 days, the only AI-related feature I used was the Gemini App (that’s not surprising considering I turned off everything else), and I have apparently used it 62 times. I’m now looking at the history of those chats, and pretty much all of them are single-question queries related to something web dev I was doing. Things like how to do a specific thing inside Kirby, or how to achieve something using a particular JS library. This is stuff one should be able to find inside documentation websites, but the search there is often awful and so after a google search, I try my luck with AI. And as I wrote somewhere else, I never copy-paste. I ask very narrow questions so that I can be pointed towards the correct answer. And once I have that, I do the coding and I re-implement everything myself. Am I against using AI? As a generative tool, yes. I refuse to ask AI to do something for me or to generate content from scratch. As a tech in general? I think it has some potentially useful applications in narrow contexts. As always, the answer is not cut-and-dry, and it can be yes or no depending on the framing and the scope. The only aspect I appreciate is the ability to ask questions in natural language. Because sometimes you have a problem or an answer you’re looking for that can’t be described in a more structured way. As for what I don’t like, how anthropomorphised these stupid tools are is definitely high on my list. I don’t want my computer to talk back or to make jokes or to say «I’m sorry». If I input a question, I want an answer back, and that’s it. I don’t want follow up questions, I don’t want some pointless preamble. I get why this happens, but I fucking hate it. This is software. I don't want my software to have a personality. I want it to perform a task and get out of my way. I also don’t like the lying, the gaslighting, and all the other crap, and I also don’t like what the AI industry is doing as a whole, but that’s a separate issue. Again, another question that has different answers depending on the scope. The idea of being able to generate images, in general, is neutral to me. It all comes down to what you use it for. There are some potential use cases that are totally fine, others are completely insane. As a whole, I think the ability to generate slop is bad, but that’s because humanity can’t be trusted to do anything the right way. As for their use in blog posts, I think stock images were useless, and I don’t see images generated with AI to be any different. Unless you have generated an image as part of the content to explain or visualise something. That’s fine, label it as a generated image and move on. That’s no different than including a render, or a sketch on paper, from a content perspective. My consumption of online content these days is so limited that I don’t have this issue. I read very few blogs, and I know they are not AI generated because I emailed the people behind them more than once, and I know what their stance is. I watch almost no YouTube, and I only read a few news sites. My strategy is to simply stay away from the digital world as much as possible, and I’m at the point where I’m considering dropping my digital consumption down to zero and quit the internet as a place for content. I have zero hope. And that is because I have zero hope in anything that’s in the hands of mega corporations. The incentives are totally skewed, and they’d do everything they can in order to keep the line go up. I don’t see people with strong morals in positions of power and so unless we decide to go full French Revolution, I see no reason for things to improve. Thank you for keeping RSS alive. You're awesome. Email me :: Sign my guestbook :: Support for 1$/month :: See my generous supporters :: Subscribe to People and Blogs

0 views
Evan Hahn 2 days ago

Notes from May 2026

My blog turned 16 this month! I did nothing to celebrate, but made some little tools and clicked some links about tech ethics. I published four little tools this month: I also did some work on Helmet, my open source project: And like every month, I wrote a few articles at Zelda Dungeon . I don’t feel I wrote anything special this month, but my colleagues put together a feature about Zelda and mental health which was very affecting! “The vast majority of tech workers, at least those who I have encountered in my many years of reporting, are not vampiric Silicon Valley tech bro caricatures [… They] both like working with tech and ultimately want to see it serve the public good.” From “They just formed the biggest tech worker union in the US. They plan to rein in AI and curb layoffs” . This “love letter to Gnutella” is both an introduction to a P2P protocol and a celebration of the culture around it. From “Affordances for me, but not for thee” : “One of the oddest parts of the AI shift is that people are much more willing to do things for LLMs that they should have been doing for human beings all along.” Accessibility, specifications, documentation, and policies are better codified now. The author calls this “dystopian”, and I agree: our motivation to do this stuff is AI or productivity, not helping our fellow human. “More importantly, whereas accessibility affordances provide new abilities for vulnerable people, an AI affordance provides new abilities for people with power. And that’s probably the heart of it.” Looking forward to being surveilled because I’m an “anti-tech extremist” . I can’t tell you how exciting it was to watch Jira add 2 + 3 . “What can I do to resist AI?” asks the AI Resist List . “Tech companies like Google, Facebook and Microsoft are ignoring data controls mandated under California law, researchers say.” “Your AI Slop Bores Me” presents an interface that looks like an LLM chatbot, but it’s entirely powered by humans. A very cute idea. I’m a very bad “image generator”, at least according to the ratings I received. I continue to be amazed by “Lest We Forget the Horrors: An Unending Catalog of Trump’s Cruelties, Collusions, Corruptions, and Crimes” . It’s so thorough. RIP to a real one: Wikinews is shutting down after 21 years . Hope you had a good May. ZIP Shrinker , a web app that shrinks ZIP files with higher compression ratios A command line tool to do (completely offline) translation Open Link in Unloaded Tab , a Firefox extension to open links without loading them png-cmp , a command line tool to compare PNG pixel data After over a year of quiet maintenance, I released version 8.2.0 with some small new features and documentation updates. In a step toward dropping GitHub, I moved the docs from a GitHub URL to helmet.js.org . “The vast majority of tech workers, at least those who I have encountered in my many years of reporting, are not vampiric Silicon Valley tech bro caricatures [… They] both like working with tech and ultimately want to see it serve the public good.” From “They just formed the biggest tech worker union in the US. They plan to rein in AI and curb layoffs” . This “love letter to Gnutella” is both an introduction to a P2P protocol and a celebration of the culture around it. From “Affordances for me, but not for thee” : “One of the oddest parts of the AI shift is that people are much more willing to do things for LLMs that they should have been doing for human beings all along.” Accessibility, specifications, documentation, and policies are better codified now. The author calls this “dystopian”, and I agree: our motivation to do this stuff is AI or productivity, not helping our fellow human. “More importantly, whereas accessibility affordances provide new abilities for vulnerable people, an AI affordance provides new abilities for people with power. And that’s probably the heart of it.” Looking forward to being surveilled because I’m an “anti-tech extremist” . I can’t tell you how exciting it was to watch Jira add 2 + 3 . “What can I do to resist AI?” asks the AI Resist List . “Tech companies like Google, Facebook and Microsoft are ignoring data controls mandated under California law, researchers say.” “Your AI Slop Bores Me” presents an interface that looks like an LLM chatbot, but it’s entirely powered by humans. A very cute idea. I’m a very bad “image generator”, at least according to the ratings I received. I continue to be amazed by “Lest We Forget the Horrors: An Unending Catalog of Trump’s Cruelties, Collusions, Corruptions, and Crimes” . It’s so thorough. RIP to a real one: Wikinews is shutting down after 21 years .

0 views

My Agent Stack For Automating My Personal Life

My agent manages my emails, SMS, WhatsApp, Telegram and pretty much everything to automate my personal life. People keep asking me how I use agents in real life. I mean the actual boring things that make a day disappear: reading WhatsApp and Telegram, finding someone's email, searching the web, drafting the intro, updating a document in Google Drive, creating a calendar event, checking who still needs an answer, and doing all of it across the same messy tools I already use. My answer is disappointingly simple. I use Codex as an operator on top of my actual life data. It has tools. It has data connectors. It has skills. It has a source of truth. It has enough permissions to act locally, and enough approval gates that it does not embarrass me in public. That is basically the setup. Tools, data connectors, skills, and taste. I used to do more of this in Claude Code but I have been moving the setup to Codex because GPT-5.5 is currently a better model for this kind of work. The switch from Claude Code to Codex is not really the story. The story is that once a model is good enough, the real leverage comes from wiring it into the world you already live in. The important part is that the agent can move across boundaries. My personal life is not in one app. It is split between Gmail, WhatsApp, Telegram, iMessage, Google Drive, Calendar, Notion, local files, random PDFs, browser sessions, and a contacts spreadsheet that is much more valuable than it looks. A few days ago a friend sent me a WhatsApp message. She was helping a fast-growing San Francisco AI startup recruit in France and wanted to connect their recruiting manager with a recruiter I know. I did not remember the recruiter's email. I did not know the latest funding news about the startup. I needed to search WhatsApp, search Gmail, find the recruiter's email, search the web, understand why the startup was credible, draft an intro email, include the two job links, show the draft to me, send the email after approval, and then text my friend that it was done. That is normally twenty minutes of annoying app switching. WhatsApp to Gmail to Google search to Gmail again to WhatsApp again. It is not hard work, but it is exactly the kind of work that burns attention because every step is a small context switch. With the agent, I asked for the outcome. It read the WhatsApp thread, searched Gmail for the recruiter's email, researched the startup's funding and recent news on the web, drafted the intro, waited for my approval, sent the email, and then texted my friend that the intro was done. The user-facing part took about ten seconds. The agent did the glue work (in seconds!) This is the killer pattern. The agent is not "answering a question." It is operating across my tools to complete a small real-world workflow (aka a "job-to-be-done") Another example is even more boring, which is why I like it. I got a new license plate for my car. I sent photos and context to Codex. It updated the car information Markdown file I keep in Google Drive, changed the license plate, added the registration notes, preserved the existing VIN, insurance, owners, and address, then uploaded the file back to Drive. That alone is useful, but the better version is what happens next. The agent can use browser automation to go update the same information everywhere else: FasTrak, the parking app, insurance portals, DMV-related forms, or any other web app that does not have a clean API. For clean systems, it should use an API or CLI. For messy systems, it can use the browser and it's so good! I also now use Computer Use from Codex. This is what personal agents are for. Not dramatic autonomy. Administrative continuity. I was always afraid of Openclaw yolo mode in the background. I appreciate being in control. The most important architectural decision I made was centralizing valuable personal information in Google Drive. For years, a lot of my knowledge lived in Notion. I like Notion as a human workspace, but I do not love it as the primary source of truth for an agent. The API works, but the workspace is too fluid: nested pages, databases, properties, permissions, formatting, backlinks, and a lot of UI-native structure that is pleasant for humans and annoying for models. So I used the Notion API to export the valuable information and move it into Google Drive. I was not trying to perfectly preserve the Notion workspace. I was trying to make the information agent-readable. Most of the useful information in Drive is Markdown or CSV, because those formats are easy for the agent to search, diff, edit, and upload back without ceremony. Google Drive became the source of truth because gogcli gives the agent a simple command line surface for Gmail, Drive, Calendar, Docs, Sheets, Contacts, and Tasks. This is an underrated point. You should not organize your knowledge only for the human UI. You should organize it for the agent's tool path. Agents like stable file IDs, text, tables, Markdown, CSVs, and commands that return JSON. If the agent can search it, download it, edit it, upload it, and cite where it came from, the data is useful. My personal data layer is embarrassingly simple. Google Drive holds the important docs, mostly as Markdown files and CSVs. Contacts live in a Google Sheet mirrored as a CSV. Notion exports land in Drive. Local instructions live in . Skills live as Markdown files in folders. The source of truth is not elegant. It is legible. A lot of personal productivity is just joining across this data. One fact is in WhatsApp. Another is in Gmail. The email address is in Contacts. The date is in Calendar. The document is in Drive. The agent becomes useful when it can cross those boundaries without asking me to be the glue. One of my best investment was to create a contact.csv with the phone number, email, LinkedIn etc. of all the people I know. The core tools are boring by design. I use gogcli for Google Workspace, wacli for WhatsApp, imsg for iMessage and SMS, Browser Use or browser automation for web apps, and AppleScript or macOS UI automation when there is no better interface. The hierarchy is simple. APIs and CLIs are best. Local files are great. Browser automation is acceptable. Screen automation is the last resort. This hierarchy matters because agents are only as reliable as their tool surface. Asking a model to click around a website is sometimes necessary, but it is not the happy path. A command like or is much easier for the model to inspect, retry, and reason about. Here is what the tool layer looks like in practice: None of this looks like science fiction. That is the point. The future of personal agents starts as a pile of commands that let the model operate the tools you already use. You want to reduce to a maximum the abstraction layers between the models and the APIs. Tools give the agent hands. Skills give it habits. A skill is just a small operating manual that tells the agent how to do a recurring task the way I like it done. My inbox-zero skill is a good example. It tells the agent to list Gmail inbox messages through gog, separate auto-archive from needs-review, show me the important emails, quote the substance, suggest archive or reply, draft replies, wait for explicit approval, send in the original thread, preserve all recipients, archive only after sending, keep replies short, never suggest calls unless I ask, and sign with "Nicolas." That is not a fancy architecture. It is a procedure. But the procedure is the product and... it's just text instructions. Without the skill, I have to be the prompt every time. I have to remind the agent not to send without approval, not to drop cc recipients, not to suggest a call, and not to sign with some weird corporate signature. With the skill, I say "run inbox zero," and the workflow already contains my taste. The important habit is that I improve the skill every time the agent makes a mistake. If it suggests a call when I hate calls, I add that rule. If it forgets to preserve cc recipients, I add that rule. If it archives too aggressively, I tighten the classification. The agent gets better because the procedure gets better. This is how personal agents become personal. Not by having a cute voice. By accumulating operational taste. The setup compounds because the mistakes become instructions. I do not want an agent that blindly replies to everyone. I want an agent that prepares the work, shows me the draft, and asks at the right moment. For most communication workflows, the loop is: read context, draft response, show me, wait for approval, send, confirm. Sometimes I let it send directly when the stakes are low. "Tell Hugo I am in Seattle next week" does not need a board meeting. But an investor email, a customer reply, an intro, or anything with social nuance should be drafted first. This is the difference between useful and terrifying. Read-only scanning is one trust tier. Drafting is another. Sending is another. Deleting, paying, signing, or changing account settings is a completely different tier. The future is not "the agent does everything." The future is "the agent does the tedious work and asks at the right moments." The killer workflow is not email. It is life inbox triage. Every few hours, I want to ask, "What did I miss?" and have the agent scan WhatsApp, Telegram, Gmail, SMS, Calendar, and the relevant Drive changes. Then I want it to tell me who needs a reply, what is urgent, what is stale, what can be ignored, what should become a calendar event, and what needs a document search. This is the perfect agent task because it is context-heavy, repetitive, cross-tool, and full of small decisions. Humans hate doing the first pass. Agents are good at first passes. Judgment still belongs to me. The result is not that my life becomes autonomous. The result is that I stop being the person manually digging through five apps to discover the three things that matter. If someone wants to reproduce my setup, this is the checklist. Install Codex. Install gogcli for Google Workspace. Install wacli for WhatsApp. Install a Telegram connector if you use Telegram. Install imsg for iMessage and SMS. Add browser automation, ideally through Browser Use or a Chrome controller. Add macOS automation through AppleScript and UI scripting. If your knowledge lives in Notion, use the Notion API to export the valuable parts into Google Drive. Then centralize the data. Make Google Drive the source of truth. Keep contacts in a Google Sheet or CSV. Keep important personal docs as searchable files. Keep local instructions. Keep small skills for recurring workflows. Then grant permissions carefully. Full Disk Access is needed for local files and app databases. Screen Recording is useful as a visual fallback. Accessibility is needed for clicking and typing in apps. These are serious permissions, so pair them with serious approval gates. Then write the operating rules. That is basically it. Tools, data connectors, skills, approval gates, and continuous improvement. The personal computer used to be app-operated. You opened the app, searched, clicked, copied, pasted, wrote, and sent. The agent-operated computer feels different. You state the intent, the agent gathers context, proposes the action, waits for approval when needed, executes, and reports back. Once you experience this, the old way feels absurd. Why am I manually searching WhatsApp, Gmail, Google Drive, and the web to send one intro? Why am I copying a license plate into five different portals? Why am I reading 100 messages to find the three that matter? The computer should do that. The setup is still ugly. The CLIs are rough. Permissions are annoying. Some connectors break. Browser automation is brittle. You have to write skills. You have to maintain a source of truth. But that is how the future usually starts. The first useful personal agents will not look like polished consumer apps. They will look like a model inside a terminal with access to your files, accounts, memories, and tools. That is what I use today, and every week I give it one more piece of my life to operate.

0 views
Stratechery 3 days ago

2026.22: Luceing Their Mind

Welcome back to This Week in Stratechery! As a reminder, each week, every Friday, we’re sending out this overview of content in the Stratechery bundle; highlighted links are free for everyone . Additionally, you have complete control over what we send to you. If you don’t want to receive This Week in Stratechery emails (there is no podcast), please uncheck the box in your delivery settings . On that note, here were a few of our favorites this week. This week’s Stratechery video is on The Inference Shift . Why Everyone Hates Luce. To say that the Jony Ive-designed Ferrari Luce, the iconic carmaker’s first electric vehicle, has faced a chilly reception is an understatement. I actually think it looks great —  for an electric car . On Dithering , John and I discuss why the real problem is that it’s branded Ferrari, and on Sharp Tech I get even more philosophical: electric cars are focused first and foremost on efficiency, and not only is that different than performance, Ferrari’s calling card, but also representative of the parts of modern society — including tech — that leave everyone feeling increasingly alienated (and why, surprisingly, AI might help). — Ben Thompson How to Monetize AI Answers. The ad business is, for me at least, endlessly fascinating, and not just because it is the most important business model in consumer tech: I think digital ads, particularly Meta-style ads that introduce you to things you never knew you wanted, a societal good. The other reason to care about ads, however, is that their economic importance means they are where the impacts of new technology are often felt first. This week’s Interview with Eric Seufert covers all this: how LLMs are changing digital ads, the changes both Google and OpenAI have made in terms of monetizing AI, and, more philosophically, why believing in ads might make one more optimistic about humanity in an AI-denominated future. — BT Social Mobility in China, and Lack Thereof.  Late last week China’s State Council announced a reform that will ease so-called “hukou restrictions” and allow migrant workers from all over the country to access social services in the cities where they work, which had long been forbidden. It’s a major reform that furthers Xi’s goal to unify the national market, and should improve the lives of millions of workers, but it also comes with plenty of questions as it’s implemented. We discussed all of it on a great episode of Sharp China this week , as well as reports that top Chinese talent in AI has been banned from leaving the country, continued capital control, and ongoing tensions with Japan and the U.S. that call to mind an ominous passage from Mao Zedong.  — AS Nvidia Earnings, The AI Stack, Nvidia’s New Reporting — Nvidia is changing its reporting to delineate between hyperscaler sales — where Nvidia is fighting commoditization — and everyone else, where Nvidia runs the whole stack. The SpaceX IPO and Data Centers in Space — There isn’t a financial model that justifies the SpaceX IPO, but data centers in space are plausible, and that might be enough. An Interview with Eric Seufert About Models and Ads, and AI’s Upside for Humanity — An Interview with Eric Seufert about building models for generative AI, why Meta’s foundational models are so important, and why understanding advertising leads to optimism about humanity’s future. How Spencer Pratt Happens — Spencer Pratt’s success in L.A. reflects his own surprising political talent, and an increasingly broken Democratic machine in California and beyond. Acquired the Podcast The Ferrari Luce How Things Fell Apart for Germany’s Nixdorf Computer Japan’s Rare Earths Island Social Mobility and Hukou Reform; US Halts Taiwan Arms Sales?; Ongoing Pressure on Japan; An American Xinhua Journalist Arrested The Knicks are in the NBA Finals, A Moment of SGA Truth, Around the League with Giannis, Bulls, and the Basketball Gods SpaceX Hype and the Elon Bargain, Nvidia and the Neoclouds, Q&A on Dropbox, Google, Ferrari Luce Backlash

0 views

Premium: What If...We're In An AI Bubble? (Part 3)

Last week I ran the second part of my three-part “What If…We’re In An AI Bubble?” series where I have been covering the scenarios that I believe could lead to the bubble popping. Here’s what I’ve discussed so far: Today I want to start with a very simple rundown of what has to happen for the AI bubble to make sense. These are all points that are rooted entirely in the projections and sales of the companies in question.  As NVIDIA intends to sell over a trillion dollars of Blackwell and Vera Rubin GPUs by the end of 2027 , it needs to have around (assuming a PUE of 1.35) 40GW of data center capacity built to support the 30GW+ of GPUs it will have sold .  With that compute being sold at around $12 million a megawatt (based on discussions with analysts and sources), that means that there must be around $435 billion in global annual compute demand to substantiate the amount of GPUs sold.  Outside of OpenAI and Anthropic, there doesn’t appear to be more than a few billion dollars of demand . Another concerning sign is that NVIDIA has had to agree to spend $30 billion in multi-year cloud compute agreements across the very partners it’s selling GPUs to ( per page 16 of its most-recent 10-Q ): The other problem is that data centers are taking way, way too long to finish , taking upwards of 24 months even for smaller 40MW builds.  This means that… Put another way, NVIDIA’s continued growth relies on people’s belief that A) these data centers get built and B) that they’ll actually make money.  Per COO Greg Brockman, OpenAI will spend around $50 billion on compute in 2026 , and I imagine Anthropic will spend in or around the same amount, especially as it’s now agreed to spend $15 billion a year on Musk’s Colossus data centers on top of whatever it spends on Google Cloud, Microsoft Azure and Amazon Web Services.  $100 billion is nowhere near enough to justify the compute being built. And while Anthropic and OpenAI have made more than $1.1 trillion in compute commitments in the next 3-5 years across Microsoft, Google, Amazon, Oracle, CoreWeave, Cerebras, Terawulf, and Cipher Mining, there’s so much more compute that needs to be sold on top of that.  Even if both doubled their spend in a year, we’d still need at least another two Anthropic or OpenAI-sized compute customers — either in aggregate or as separate companies — at a time when I can’t find a single other company spending even a hundred million dollars a year on compute. Most AI startups (and customers) want to pay Anthropic or OpenAI directly to access their models , which means that either Anthropic and OpenAI need to use roughly twice the amount of compute they do today and then some to meet the capacity being built. This will require them to do something either historic or impossible. This is not hyperbole! OpenAI, per The Information , plans to burn $852 billion through the end of 2030. Anthropic has, per The Information, agreed to spend $330 billion on compute on Microsoft, Google, and Amazon , at least another $30 billion on compute with CoreWeave , and another $63 billion in TPUs bought from Broadcom .  To reach this point, Anthropic projects it will hit $174 billion in annual revenue by the end of 2029, and OpenAI $284 billion . Both have made ridiculous claims of profitability ( with Anthropic actively conning investors with a “profitable” quarter based on discounted bills ) in the next few years that are immaterial to the larger point that they need actual, real cash to meet their obligations.  This is, again, not hyperbole. If we assume that the services in question are profitable, sustainable businesses, then revenues attached to AI services must exceed those driven by AI compute by a reasonable margin. It isn’t enough for us to have a few AI companies that spend a lot more on compute than they take in revenue, because at some point venture capital subsidies will run dry.  This isn’t happening. Putting aside the profitability part for a second, OpenAI and Anthropic account for 89% of all AI startup revenues , with the nearest competitor being Cursor with its pathetic $3 billion in annualized revenue . These are rookie numbers. They are insufficient. We need so much more than this. Again, not hyperbole! These are OpenAI and Anthropic’s own revenue projections — $184 billion and $174 billion respectively — that they expect to hit by the end of 2029. These are the same projections that have been used to make their $1.1 trillion in compute commitments, much of which make up 50% of Google, Amazon, and Microsoft’s remaining performance obligations : These commitments reflect expected revenue and demand for OpenAI and Anthropic’s services, but they’re commitments, which means that they need to be paid even if that demand doesn’t exist.  This is a huge problem for these companies. If they buy too much compute and don’t have the demand and revenue to support it, they’ll go bankrupt.  To be clear, that’s not my opinion, it’s what Anthropic CEO Dario Amodei said to Dwarkesh Patel in February, emphasis mine: That is not good! As I’ve covered before , buying compute is a knife-catching game where you have to guess how much you need for a particular year, and if you guess correctly you don’t lose as much money but if you guess wrong you run out of money.  It should be far more worrying to executives that the single-largest AI company is basically saying that if he mistimes growth his company explodes! Per Business Insider , Uber COO Andrew Macdonald said this weekend that it was becoming “harder to justify AI costs within the company”: Anthropic’s meteoric revenue growth has come from both AI startups burning more tokens ( as Opus 4.7 appears to burn more than ever ) and large organizations doing some form of “token-maxxing,” meaning that they tell their employees to use AI as much as they want, usually with KPIs that specifically track AI usage, as is the case at Meta , Amazon, and Zillow . Even organizations that aren’t actively incentivizing their engineers to burn more tokens are finding they’re blowing through their budgets at record speed. The situation with Uber’s COO was caused by his CTO saying back in April that the company had burned through its entire annual token budget in four months. Similarly, my reporting on Zillow’s AI spend showed that it will likely max out its annual Cursor budget by the end of May. The problem, as Macdonald said, is that nobody can seem to track all of this spend to an actual return on investment. This isn’t a situation where somebody is saying “the ROI is low but improving” or “we’re on the path to working that out,” but “it’s very hard to actually draw a line between “what we’ve spent” and “a reason we’re spending it.” This makes it hard for Uber to say how much it should reduce its token budgets. If you can’t measure the return on investment, how do you measure how much you’re meant to spend? What is “enough”? Because right now it’s clear that whatever they’re spending is too much , which means that there’s a ceiling to Anthropic and OpenAI’s revenue story.  OpenAI and especially Anthropic cannot afford for this conversation to be happening, because it suggests there’s a ceiling to the amount that people will spend on AI. It appears there’s a limit to which organizations can be abused and manipulated into believing that “the future is here,” and that limit is when they pay millions for something that doesn’t appear to have a measurable return on investment.  Anthropic and OpenAI need organizations to willingly spend 10% to 100% of their headcount on AI, as their revenue projections are clearly tied to every organization maintaining a significant spend on tokens in perpetuity.  There’re really two problems: This is budgetary poison. Right now, the vast majority of AI token spend is experimental , and if companies are already hesitating at the amounts they’re spending, Anthropic has no way to keep growing, and they also have no super secret models or harnesses or products that are going to reverse this trend. Nobody knows why they’re spending so much money or even how much money they might spend in a given month , which makes it tough to view Anthropic’s ( suspicious ) revenue growth as anything but a chaotic money-dump driven by CEOs that don’t know what their companies actually do and have been beguiled by the AI grift machine . And as I wrote up last week , OpenAI had a negative 122% operating margin in Q1 2026, and ChatGPT growth has stalled. It is unclear what its API revenue is, but it’s likely much less than Anthropic despite shoving its enterprise customers onto token-based billing not long after they did. As I’ve said: this cannot happen, and neither Anthropic nor OpenAI can afford to slow down. Their revenues must grow to over $100 billion by 2028, as their compute commitments demand it. Their growth must continue.  It’s been a little under four years of endless confidence about the inevitable growth of generative AI, and by extension the eternal success and growth of OpenAI. Yet in reality, its economics have only ever soured, and its growth appears to be collapsing.  In October 2024, The Information reported that OpenAI believed it would turn profitable in 2029, that its total losses between 2023 and 2028 would be $44 billion , and that its (non-GAAP, every one of these numbers is non-GAAP) gross margin would be 41% in 2024, though it would end up being a point lower at 40% in the end. OpenAI would then project a gross margin of 49% for 2025… but it ended up at 33% anyway .  OpenAI would also say on September 5 2025 that it would actually burn $115 billion through 2029 , but that “burn” assumed that it would have revenues of $60 billion in 2027, $100 billion in 2028, $145 billion in 2029, and $200 billion in 2030, when it would “become profitable” in some undiscussed manner. Two weeks later on September 19 2025, The Information would report that actually OpenAI would spend “about $450 billion to rent servers through 2030,” but not otherwise update the burn-rate. On November 4, 2025 , OpenAI CEO Sam Altman would say that the company had hit $20 billion in ARR and had made $1.4 trillion in commitments “over the next 8 years,” and a few months later On February 20, 2026 , OpenAI would claim that it had targeted “around $600 billion in compute commitments by 2030.” The very same day, The Information would report that it planned to spend $665 billion on compute through 2030 , that it missed gross margin projections (without sharing what those margins might be), and that ChatGPT had hit 910 million weekly active users that month, 90 million short of its goal of 1 billion by the end of 2025. It’s very obvious by now that OpenAI has been making up all of its projections, and that none of the numbers actually add up. My own reporting from November 2025 from actual Azure personnel suggests that OpenAI’s Q1 to Q3 revenues were billions lower than every other reported figure, and I think it’s likely that OpenAI is overstating its revenues.  In any case, on May 22, 2026 , The Information would report that OpenAI’s Q1 2026 operating margin was negative 122%, and that its Q1 average weekly active users (WAUs) sat at 905 million — suggesting that growth has stalled. OpenAI had anticipated that it would cross the one billion WAU mark by the end of 2025 — and it blamed its failure to do so on fiercer competition, primarily from Google’s Gemini. For OpenAI to afford its compute commitments, it has to make or raise $852 billion in the next four years. It must have that cashflow, or it will run out of money or be sued out of existence by its numerous counterparties from CoreWeave, Microsoft, Amazon, and Cerebras. In the final part, I’m going to get into the depths of destruction — the unraveling of the greater data center debt industry, the massive damage to private credit to come, potential shareholder lawsuits against NVIDIA, and the consequences of the deaths of OpenAI and Anthropic. What If…We’re in an AI Bubble? I also want to add that I realize three headlines didn’t make the cut — what if there’s not a bailout, what if I’m wrong, and what if I’m right — and I intend to cover all three of them in future free newsletters.  Nevertheless, today’s is an absolute beast, a 16,000 word conclusion to the first multi-part Where’s Your Ed At Premium.  What If The AI Industry Moves To Entirely Token-Based Billing?  What If Organizations Can’t Afford To Keep Spending On AI? What If The AI Capacity Crunch Never Ends (And Data Centers Aren’t Getting Built)? What If CoreWeave Can’t Keep Up With Its Capacity Demands? What If Hyperscalers Can’t Build Data Centers Very Fast? What If Hyperscalers Have Warehouses of Uninstalled GPUs? What If Hyperscalers Write Off A Large Chunk of GPUs? What If Data Center Construction Demand Collapses?  What If Venture Capital Funding Stops Flowing To AI Startups? What Would Make Venture Capital Stop Funding AI Startups? What If Most AI Startups Go To Zero? Scenario: OpenAI and Anthropic Go Full FTX, Scooping Up Dying AI Startups To Keep The Industry Afloat With Circular Financing Scenario: Venture Capital’s Post-AI Depression What If Inference Isn’t Profitable? AI Has Become An Existential Reckoning For The Valley NVIDIA’s customers are taking years to even begin making back the billions of dollars its chips and the associated construction costs. NVIDIA is selling far more GPUs every quarter than can realistically be installed in the space of a year. NVIDIA’s revenue stream is entirely based on organizations forecasting demand years into the future. NVIDIA’s revenues are, by extension, dependent on how long organizations believe that building data centers is a good idea. NVIDIA is absolutely, without a doubt, warehousing at least a million Blackwell GPUs . It’s difficult-to-impossible to actually measure the ROI of AI spend. It’s difficult-to-impossible to actually know how much it’ll cost to complete a specific task with AI. What if data center debt stops being issued? What if private credit had to write off most of its data center loans? What if the AI bubble blows up Taiwan’s ODM server manufacturers? What if NVIDIA is misrepresenting how many GPUs are shipped, sold and operational? What if OpenAI and Anthropic don’t go public? What if Oracle doesn’t get paid by OpenAI? What If OpenAI Dies? What if Anthropic Dies?

0 views