Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
12
I count my lucky stars that SUSE's pricing for Rancher and Harvester was so ridiculous over the top for our situation. If they hadn't reached for those million-dollar contracts, we'd probably be stuck in enterprise vendor hell forever, buying over-priced consulting services for Kubernetes and VM tooling. That would have sucked. Now that might sound sarcastic, but I'm being earnest here. So many of the best opportunities in life only reveal themselves when the easy path is blocked. We are creatures in search of the least resistance, so to reach higher, we usually need a little push. That dance with the enterprisey sales people at SUSE was such a push for our cloud exit adventure at 37signals. Because it clarified our values and cleared our minds. We will not build our critical infrastructure on top of anything but open source, and it has to be simple enough that our whole team can understand it. If such tools do not exist, we will build them. Thus it is with great pleasure I announce...
a year ago

More from David Heinemeier Hansson

It burns

The first time we had to evacuate Malibu this season was during the Franklin fire in early December. We went to bed with our bags packed, thinking they'd probably get it under control. But by 2am, the roaring blades of fire choppers shaking the house got us up. As we sped down the canyon towards Pacific Coast Highway (PCH), the fire had reached the ridge across from ours, and flames were blazing large out the car windows. It felt like we had left the evacuation a little too late, but they eventually did get Franklin under control before it reached us. Humans have a strange relationship with risk and disasters. We're so prone to wishful thinking and bad pattern matching. I remember people being shocked when the flames jumped the PCH during the Woolsey fire in 2017. IT HAD NEVER DONE THAT! So several friends of ours had to suddenly escape a nightmare scenario, driving through burning streets, in heavy smoke, with literally their lives on the line. Because the past had failed to predict the future. I feel into that same trap for a moment with the dramatic proclamations of wind and fire weather in the days leading up to January 7. Warning after warning of "extremely dangerous, life-threatening wind" coming from the City of Malibu, and that overly-bureaucratic-but-still-ominous "Particularly Dangerous Situation" designation. Because, really, how much worse could it be? Turns out, a lot. It was a little before noon on the 7th when we first saw the big plumes of smoke rise from the Palisades fire. And immediately the pattern matching ran astray. Oh, it's probably just like Franklin. It's not big yet, they'll get it out. They usually do. Well, they didn't. By the late afternoon, we had once more packed our bags, and by then it was also clear that things actually were different this time. Different worse. Different enough that even Santa Monica didn't feel like it was assured to be safe. So we headed far North, to be sure that we wouldn't have to evacuate again. Turned out to be a good move. Because by now, into the evening, few people in the connected world hadn't started to see the catastrophic images emerging from the Palisades and Eaton fires. Well over 10,000 houses would ultimately burn. Entire neighborhoods leveled. Pictures that could be mistaken for World War II. Utter and complete destruction. By the night of the 7th, the fire reached our canyon, and it tore through the chaparral and brush that'd been building since the last big fire that area saw in 1993. Out of some 150 houses in our immediate vicinity, nearly a hundred burned to the ground. Including the first house we moved to in Malibu back in 2009. But thankfully not ours. That's of course a huge relief. This was and is our Malibu Dream House. The site of that gorgeous home office I'm so fond to share views from. Our home. But a house left standing in a disaster zone is still a disaster. The flames reached all the way up to the base of our construction, incinerated much of our landscaping, and devoured the power poles around it to dysfunction. We have burnt-out buildings every which way the eye looks. The national guard is still stationed at road blocks on the access roads. Utility workers are tearing down the entire power grid to rebuild it from scratch. It's going to be a long time before this is comfortably habitable again. So we left. That in itself feels like defeat. There's an urge to stay put, and to help, in whatever helpless ways you can. But with three school-age children who've already missed over a months worth of learning from power outages, fire threats, actual fires, and now mudslide dangers, it was time to go. None of this came as a surprise, mind you. After Woolsey in 2017, Malibu life always felt like living on borrowed time to us. We knew it, even accepted it. Beautiful enough to be worth the risk, we said.  But even if it wasn't a surprise, it's still a shock. The sheer devastation, especially in the Palisades, went far beyond our normal range of comprehension. Bounded, as it always is, by past experiences. Thus, we find ourselves back in Copenhagen. A safe haven for calamities of all sorts. We lived here for three years during the pandemic, so it just made sense to use it for refuge once more. The kids' old international school accepted them right back in, and past friendships were quickly rebooted. I don't know how long it's going to be this time. And that's an odd feeling to have, just as America has been turning a corner, and just as the optimism is back in so many areas. Of the twenty years I've spent in America, this feels like the most exciting time to be part of the exceptionalism that the US of A offers. And of course we still are. I'll still be in the US all the time on both business, racing, and family trips. But it won't be exclusively so for a while, and it won't be from our Malibu Dream House. And that burns.

2 days ago 6 votes
Waiting on red

Americans often laugh when they see how often Danes will patiently, obediently wait on the little red man to turn green before crossing an empty intersection, in the rain, even at night. Nobody is coming! Why don't you just cross?! It seems silly, but the underlying philosophy is anything but. It's load bearing for a civil society like Denmark. Because doing the right thing every time can be put on autopilot, and when most people follow even the basic norms consistently, the second-order effects are profound. Like the fact that Copenhagen is one of the absolute safest major cities in the world. But the Danes also know that norms fray if they're not enforced, so they vigorously pursue even small infractions. The Danish police regularly celebrating ticketing bicyclists making even minor mistakes (like driving instead of dragging their bike on the sidewalk). And the metro is constantly being patrolled for fare evaders and antisocial behavior. It's broken windows theory on steroids. And it works. When we were living in the city for three years following the pandemic, the most startling difference to major US cities was the prevalence of unattended children everywhere, at all hours. Our oldest was just nine years-old when he started taking the metro alone, even at night. How many American parents would feel comfortable letting their nine-year old take the L in Chicago or the subway in Manhattan? I don't know any. And as a result, you just don't see any unattended children do this. But in Copenhagen it's completely common place. This is the prize of having little tolerance for antisocial behavior in the public space. When you take away the freedom from crackheads and bums to smoke up on the train or sleep in the park, you grant the freedom to nine-year olds to roam the city and for families to enjoy the park at dusk. This is the fundamental error of suicidal empathy. That tolerance of the deranged and dangerous few can be kept a separate discussion from the freedom and safety of the many. These are oppositional forces. The more antisocial behavior you excuse, the further families will retract into their protective shell. And suddenly there are no longer children around in the public city space or any appetite for public transit. Maybe you have to become a parent to really understand this. I admit that I didn't give this nearly the same attention before coming a father of three. But the benefit isn't exclusively about the freedom and safety enjoyed by your own family, it's also about the ambient atmosphere of living in a city where children are everywhere. It's a special form of life-affirming luxury, and it's probably the thing I've missed most about Copenhagen since we went back to the US. What's interesting is how much active effort it takes to maintain this state of affairs. The veneer of civil society is surprisingly thin. Norms fray quickly if left unguarded. And it's much harder to reestablish their purchase on society than to protect them from disappearing in the first place. But I also get that it's hard to connect the dots from afar, though. Many liberals in America keep Denmark as some mythical place where all their policy dreams have come true, without ever wrestling much with what it takes to maintain the social trust that allows those policies to enjoy public support. The progressive Nirvana of Denmark is built on a highly conservative set of norms and traditions. It's ying and yang. So if you're committed to those progressive outcomes in America, whether it's the paternity leave, the independent children, or the amazing public transit system, you ought to consider what conservative values it makes sense to accept as enablers rather than obstacles.

a week ago 18 votes
MEGA

Trump is back at the helm of the United States, and the majority of Americans are optimistic about the prospect. Especially the young. In a poll by CBS News, it's the 18-29 demographic that's most excited, with a whopping two-thirds answering in the affirmative to being optimistic about the next four years under Trump. And I'm right there with them. The current American optimism is infectious! While Trump has undoubtedly been the catalyst, this is a bigger shift than any one person. After spending so long lost in the wilderness of excessive self-criticism and self-loathing, there's finally a broad coalition of the willing working to get the mojo back. This is what's so exhilarating about America. The big, dramatic swings. The high stakes. The long shots. And I like this country much better when it's confident in that inherent national character. Of course all this is political. And of course Trump is triggering for many. Just like his opponent would have been if she had won. But this moment is not just political, it's beyond that. It's economic, it's entrepreneurial, it's technological. Optimism is infectious. As someone with a foot on both the American and European continent, I can't help being jealous with my euro leg. Europe is stuck with monumental levels of pessimism at the moment, and it's really sad to see. But my hope is that Europe, like usual, is merely a few years behind the American revival in optimism. That it's coming to the old world eventually. This is far more an article of faith than of analysis, mind you. I can also well imagine Europe sticking with Eurocrat thinking, spinning its wheels with grand but empty proclamations, issuing scorning but impotent admonishments of America, and doubling down on the regulatory black hole. Neither path is given. Europe was competitive with America on many economic terms as recently as 15 years ago. But Europe also lacks the ability to change course quite like the Americans. So the crystal ball is blurry. Personally, I choose faith. Optimism must win. Pessimism is literally for losers.

a week ago 29 votes
Failed integration and the fall of multiculturalism

For decades, the debate in Denmark around  problems with mass immigration was stuck in a self-loathing blame game of "failed integration". That somehow, if the Danes had just tried harder, been less prejudice, offered more opportunities, the many foreigners with radically different cultures would have been able to integrate successfully. If not in the first generation, then the second. For much of this time, I thought that was a reasonable thesis. But reality has proved it wrong. If literally every country in Europe has struggled in the same ways, and for decades on end, to produce the fabled "successful integration", it's not a compelling explanation that it's just because the Danes, Swedes, Norweigans, Germans, French, Brits, or Belgians just didn't try hard enough. It's that the mission, on the grand and statistical scale, was impossible in many cases. As Thomas Sowell tells us, this is because there are no solutions to intractable, hard problems like cultural integration between wildly different ways of living. Only trade offs. Many of which are unfavorable to all parties. But by the same token, just because the overall project of integrating many of the most divergent cultures from mass immigrations has failed, there are many individual cases of great success. Much of the Danish press, for example, has for years propped up the hope of broad integration success by sharing hopeful, heartwarming stories of highly successful integration. And you love to see it. Heartwarming anecdotes don't settle trade offs, though. They don't prove a solution or offer a conclusion either. I think the conclusion at this point is clear. First, cultural integration, let alone assimilation, is incredibly difficult. The more divergent the cultures, the more difficult the integration. And for some combinations, it's outright impossible. Second, the compromise of multiculturalism has been an abject failure in Europe. Allowing parallel cultures to underpin parallel societies is poison for the national unity and trust. Which brings us to another bad social thesis from the last thirty-some years: That national unity, character, and belonging not only isn't important, but actively harmful. That national pride in history, traditions, and culture is primarily an engine of bigotry. What a tragic thesis with catastrophic consequences. But at this point, there's a lot of political capital invested into all these bad ideas. In sticking with the tired blame game. Thinking that what hasn't worked for fifty years will surely start working if we give it five more.  Now, I actually have a nostalgic appreciation for the beautiful ideals behind such hope for humanity, but I also think that at this point it is as delusional as it is dangerous. And I think it's directly responsible for the rise of so-called populist movements all over Europe. They're directly downstream from the original theses of success in cultural integration going through just-try-harder efforts as well as the multicultural compromise. A pair of ideas that had buy-in across much of the European board until reality simply became too intolerable for too many who had to live with the consequences. Such widespread realization doesn't automatically correct the course of a societal ship that's been sailing in the wrong direction for decades, of course. The playbook that took DEI and wokeness to blitzkrieg success in the States, by labeling any dissent to those ideologies racist or bigoted, have also worked to hold the line on the question of mass immigration in Europe until very recently.  But I think the line is breaking in Europe, just as it recently did in America. The old accusations have finally lost their power from years of excessive use, and suppressing the reality that many people can see with their own eyes is getting harder. I completely understand why that makes people anxious, though. History is full of examples of combative nationalism leading us to dark edges. And, especially in Germany, I can understand the historical hesitation when there's even a hint of something that sounds like what they heard in the 30s. But you can hold both considerations in your head at the same time without losing your wits. Mass immigration to Europe has been a failure, and the old thesis of naive hope has to get replaced by a new strategy that deals with reality. AND that not all proposed fixes by those who diagnosed the situation early are either sound or palatable. World history is full of people who've had the correct diagnosis but a terrible prescription. And I think it's fair to say that it's not even obvious what the right prescription is at this point! Vibrant, strong societies surely benefit from some degree of immigration. Especially from culturally-compatible regions based on national and economic benefit. But whatever the specific trade-offs taken from here, it seems clear that for much of Europe, they're going to look radically different than they've done in the past three decades or so. Best get started then.

a week ago 31 votes
The social media censorship era is over (for now)

Mark Zuckerberg just announced a stunning pivot for Meta's approach to social media censorship. Here's what he's going to do: Replace third-party fact checkers with community notes ala X. Allow free discussion on immigration, gender, and other topics that were heavily censored in the past, as well as let these discussions freely propagate (and go viral). Focus moderation on illegal activities, like child exploitation, frauds, and scams, instead of political transgressions. Relocate the moderation team from California to Texas to address political bias from within the team. This new approach is going to govern all the Meta realms, from Facebook to Threads to Instagram. Meaning it'll affect the interactions of some three billion people around the globe. In other words, this is huge. As to be expected, many are highly skeptical of Zuckerberg's motives. And for good reason. Despite making a soaring speech to the values of free speech back in 2019, Meta, together with Twitter, became one of the primary weapons for a political censorship regime that went into overdrive during the pandemic. Both Meta and Twitter received direct instructions from the US government, among other institutions, on what was to be considered allowable speech and what was to be banned. The specifics shifted over those awful years, but everything from questioning the origins of the Covid virus to disputing vaccine efficacy to objections on mass migration to the Hunter Biden laptop leak all qualified for heavy-handed intervention. The primary rhetorical fig leaves for this censorship regime was "hate speech" and "misinformation". Terms that almost immediately lost all objective content, and turned into mere descriptors of "speech we don't like". Either because it was politically inconvenient or because it offended certain holy tenants of the woke religion that reigned at the time. But that era is now over. Between Meta and X, the gravity of the global discourse has swung dramatically in favor of free expression. I suspect that YouTube and Reddit will eventually follow suit as well. But even if they don't, it won't really matter. The forbidden opinions and inconvenient information will still be able to reach a wide audience. That's a momentous and positive moment for the world. And it's a particularly proud moment for America, since this is all downstream from the country's first amendment protection of free speech. But it's also adding to the growing chasm between America and Europe. And the United Kingdom in particular. While America is recovering from the authoritarian grip on free speech in terms of both social media policies and broader social consequences (remember cancel culture?), the Brits are doubling down. Any post on social media made in Britain is liable to have those cute little bobbies show up at your door with a not-so-cute warrant for your arrest. The delusional UK police commissioner is even threatening to "come after" people from around the world, if they write bad tweets. And Europe isn't far behind. Thierry Breton, the former European Commissioner, spent much of last year threatening American tech companies, and Elon Musk in particular, with draconian sanctions, if they failed to censor on the EU's behest. He has thankfully since been dismissed, but the sentiment of censorship is alive and well in the EU. This is why the world needs America. From the UK to the EU to Brazil, China, Russia, and Iran, political censorship is very popular. And for a couple of dark years in the US, it looked like the whole world was about to be united in an authoritarian crackdown on speech of all sorts. But Elon countered the spell. His acquisition of Twitter and its transformation into X was the pivotal moment for both American and global free speech. And if you allow yourself to zoom out from the day-to-day antics of the meme lord at large, you should be able to see clearly how the timeline split. I know that's hard to do for a lot of people who've traded in their Trump Derangement Syndrome diagnosis for a Musk Derangement Syndrome variety (or simply added it to their inventory of mental challenges). And I get it. It's hard to divorce principles from people! We're all liable to mix and confuse the two. And speaking of Trump, which, to be honest, I try not to do too often, because I know how triggering he is,  credit is still due. There's no way this incredible vibe shift would have happened as quickly or as forcefully without his comeback win. Now I doubt that any of his political opponents are going to give him any credit for this, even if they do perhaps quietly celebrate the pivot on free speech. And that's OK. I don't expect miracles, and we don't need them either. You don't need to love every champion of your principles to quietly appreciate their contributions. Which very much reminds me of the historic lawsuit that the Jewish lawyers at the ACLU (in its former glory) fought to allow literal nazis to match in the streets of Skokie, Illinois. That case goes to the crux of free speech. That in order for you to voice your dissent on Trump or Musk or whatever, you need the protection of the first amendment to cover those who want to dissent in the opposite direction too. That's a principle that's above the shifting winds and vibes of whoever is in power. It's entire purpose is to protect speech that's unpopular with the rulers of the moment. And as we've seen, electoral fortunes can change! It's in your own self interest to affirm a set of rules for participation in the political debate that live beyond the what's expedient for partisan success in the short term. I for one am stoked about Meta's pivot on censorship. I've historically not exactly been Mark Zuckerberg's biggest fan, and I do think it's fair to question the authenticity of him and this move, but I'm not going to let any of that get in the way of applauding this monumental decision. The world needs America and its exceptional principles more than ever. I will cheer for Zuckerberg without reservation when he works in their service. Now how do we get the UK and the EU to pivot as well?

3 weeks ago 34 votes

More in programming

Adding auto-generated cover images to EPUBs downloaded from AO3

I was chatting with a friend recently, and she mentioned an annoyance when reading fanfiction on her iPad. She downloads fic from AO3 as EPUB files, and reads it in the Kindle app – but the files don’t have a cover image, and so the preview thumbnails aren’t very readable: She’s downloaded several hundred stories, and these thumbnails make it difficult to find things in the app’s “collections” view. This felt like a solvable problem. There are tools to add cover images to EPUB files, if you already have the image. The EPUB file embeds some key metadata, like the title and author. What if you had a tool that could extract that metadata, auto-generate an image, and use it as the cover? So I built that. It’s a small site where you upload EPUB files you’ve downloaded from AO3, the site generates a cover image based on the metadata, and it gives you an updated EPUB to download. The new covers show the title and author in large text on a coloured background, so they’re much easier to browse in the Kindle app: If you’d find this helpful, you can use it at alexwlchan.net/my-tools/add-cover-to-ao3-epubs/ Otherwise, I’m going to explain how it works, and what I learnt from building it. There are three steps to this process: Open the existing EPUB to get the title and author Generate an image based on that metadata Modify the EPUB to insert the new cover image Let’s go through them in turn. Open the existing EPUB I’ve not worked with EPUB before, and I don’t know much about it. My first instinct was to look for Python EPUB libraries on PyPI, but there was nothing appealing. The results were either very specific tools (convert EPUB to/from format X) or very unmaintained (the top result was last updated in April 2014). I decied to try writing my own code to manipulate EPUBs, rather than using somebody else’s library. I had a vague memory that EPUB files are zips, so I changed the extension from .epub to .zip and tried unzipping one – and it turns out that yes, it is a zip file, and the internal structure is fairly simple. I found a file called content.opf which contains metadata as XML, including the title and author I’m looking for: <?xml version='1.0' encoding='utf-8'?> <package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="uuid_id"> <metadata xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"> <dc:title>Operation Cameo</dc:title> <meta name="calibre:timestamp" content="2025-01-25T18:01:43.253715+00:00"/> <dc:language>en</dc:language> <dc:creator opf:file-as="alexwlchan" opf:role="aut">alexwlchan</dc:creator> <dc:identifier id="uuid_id" opf:scheme="uuid">13385d97-35a1-4e72-830b-9757916d38a7</dc:identifier> <meta name="calibre:title_sort" content="operation cameo"/> <dc:description><p>Some unusual orders arrive at Operation Mincemeat HQ.</p></dc:description> <dc:publisher>Archive of Our Own</dc:publisher> <dc:subject>Fanworks</dc:subject> <dc:subject>General Audiences</dc:subject> <dc:subject>Operation Mincemeat: A New Musical - SpitLip</dc:subject> <dc:subject>No Archive Warnings Apply</dc:subject> <dc:date>2023-12-14T00:00:00+00:00</dc:date> </metadata> … That dc: prefix was instantly familiar from my time working at Wellcome Collection – this is Dublin Core, a standard set of metadata fields used to describe books and other objects. I’m unsurprised to see it in an EPUB; this is exactly how I’d expect it to be used. I found an article that explains the structure of an EPUB file, which told me that I can find the content.opf file by looking at the root-path element inside the mandatory META-INF/container.xml file which is every EPUB. I wrote some code to find the content.opf file, then a few XPath expressions to extract the key fields, and I had the metadata I needed. Generate a cover image I sketched a simple cover design which shows the title and author. I wrote the first version of the drawing code in Pillow, because that’s what I’m familiar with. It was fine, but the code was quite flimsy – it didn’t wrap properly for long titles, and I couldn’t get custom fonts to work. Later I rewrote the app in JavaScript, so I had access to the HTML canvas element. This is another tool that I haven’t worked with before, so a fun chance to learn something new. The API felt fairly familiar, similar to other APIs I’ve used to build HTML elements. This time I did implement some line wrapping – there’s a measureText() API for canvas, so you can see how much space text will take up before you draw it. I break the text into words, and keeping adding words to a line until measureText tells me the line is going to overflow the page. I have lots of ideas for how I could improve the line wrapping, but it’s good enough for now. I was also able to get fonts working, so I picked Georgia to match the font used for titles on AO3. Here are some examples: I had several ideas for choosing the background colour. I’m trying to help my friend browse her collection of fic, and colour would be a useful way to distinguish things – so how do I use it? I realised I could get the fandom from the EPUB file, so I decided to use that. I use the fandom name as a seed to a random number generator, then I pick a random colour. This means that all the fics in the same fandom will get the same colour – for example, all the Star Wars stories are a shade of red, while Star Trek are a bluey-green. This was a bit harder than I expected, because it turns out that JavaScript doesn’t have a built-in seeded random number generator – I ended up using some snippets from a Stack Overflow answer, where bryc has written several pseudorandom number generators in plain JavaScript. I didn’t realise until later, but I designed something similar to the placeholder book covers in the Apple Books app. I don’t use Apple Books that often so it wasn’t a deliberate choice to mimic this style, but clearly it was somewhere in my subconscious. One difference is that Apple’s app seems to be picking from a small selection of background colours, whereas my code can pick a much nicer variety of colours. Apple’s choices will have been pre-approved by a designer to look good, but I think mine is more fun. Add the cover image to the EPUB My first attempt to add a cover image used pandoc: pandoc input.epub --output output.epub --epub-cover-image cover.jpeg This approach was no good: although it added the cover image, it destroyed the formatting in the rest of the EPUB. This made it easier to find the fic, but harder to read once you’d found it. An EPUB file I downloaded from AO3, before/after it was processed by pandoc. So I tried to do it myself, and it turned out to be quite easy! I unzipped another EPUB which already had a cover image. I found the cover image in OPS/images/cover.jpg, and then I looked for references to it in content.opf. I found two elements that referred to cover images: <?xml version="1.0" encoding="UTF-8"?> <package xmlns="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="PrimaryID"> <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf"> <meta name="cover" content="cover-image"/> … </metadata> <manifest> <item id="cover-image" href="images/cover.jpg" media-type="image/jpeg" properties="cover-image"/> … </manifest> </package> This gave me the steps for adding a cover image to an EPUB file: add the image file to the zipped bundle, then add these two elements to the content.opf. Where am I going to deploy this? I wrote the initial prototype of this in Python, because that’s the language I’m most familiar with. Python has all the libraries I need: The zipfile module can unpack and modify the EPUB/ZIP The xml.etree or lxml modules can manipulate XML The Pillow library can generate images I built a small Flask web app: you upload the EPUB to my server, my server does some processing, and sends the EPUB back to you. But for such a simple app, do I need a server? I tried rebuilding it as a static web page, doing all the processing in client-side JavaScript. That’s simpler for me to host, and it doesn’t involve a round-trip to my server. That has lots of other benefits – it’s faster, less of a privacy risk, and doesn’t require a persistent connection. I love static websites, so can they do this? Yes! I just had to find a different set of libraries: The JSZip library can unpack and modify the EPUB/ZIP, and is the only third-party code I’m using in the tool Browsers include DOMParser for manipulating XML I’ve already mentioned the HTML <canvas> element for rendering the image This took a bit longer because I’m not as familiar with JavaScript, but I got it working. As a bonus, this makes the tool very portable. Everything is bundled into a single HTML file, so if you download that file, you have the whole tool. If my friend finds this tool useful, she can save the file and keep a local copy of it – she doesn’t have to rely on my website to keep using it. What should it look like? My first design was very “engineer brain” – I just put the basic controls on the page. It was fine, but it wasn’t good. That might be okay, because the only person I need to be able to use this app is my friend – but wouldn’t it be nice if other people were able to use it? If they’re going to do that, they need to know what it is – most people aren’t going to read a 2,500 word blog post to understand a tool they’ve never heard of. (Although if you have read this far, I appreciate you!) I started designing a proper page, including some explanations and descriptions of what the tool is doing. I got something that felt pretty good, including FAQs and acknowledgements, and I added a grey area for the part where you actually upload and download your EPUBs, to draw the user’s eye and make it clear this is the important stuff. But even with that design, something was missing. I realised I was telling you I’d create covers, but not showing you what they’d look like. Aha! I sat down and made up a bunch of amusing titles for fanfic and fanfic authors, so now you see a sample of the covers before you upload your first EPUB: This makes it clearer what the app will do, and was a fun way to wrap up the project. What did I learn from this project? Don’t be scared of new file formats My first instinct was to look for a third-party library that could handle the “complexity” of EPUB files. In hindsight, I’m glad I didn’t find one – it forced me to learn more about how EPUBs work, and I realised I could write my own code using built-in libraries. EPUB files are essentially ZIP files, and I only had basic needs. I was able to write my own code. Because I didn’t rely on a library, now I know more about EPUBs, I have code that’s simpler and easier for me to understand, and I don’t have a dependency that may cause problems later. There are definitely some file formats where I need existing libraries (I’m not going to write my own JPEG parser, for example) – but I should be more open to writing my own code, and not jumping to add a dependency. Static websites can handle complex file manipulations I love static websites and I’ve used them for a lot of tasks, but mostly read-only display of information – not anything more complex or interactive. But modern JavaScript is very capable, and you can do a lot of things with it. Static pages aren’t just for static data. One of the first things I made that got popular was find untagged Tumblr posts, which was built as a static website because that’s all I knew how to build at the time. Somewhere in the intervening years, I forgot just how powerful static sites can be. I want to build more tools this way. Async JavaScript calls require careful handling The JSZip library I’m using has a lot of async functions, and this is my first time using async JavaScript. I got caught out several times, because I forgot to wait for async calls to finish properly. For example, I’m using canvas.toBlob to render the image, which is an async function. I wasn’t waiting for it to finish, and so the zip would be repackaged before the cover image was ready to add, and I got an EPUB with a missing image. Oops. I think I’ll always prefer the simplicity of synchronous code, but I’m sure I’ll get better at async JavaScript with practice. Final thoughts I know my friend will find this helpful, and that feels great. Writing software that’s designed for one person is my favourite software to write. It’s not hyper-scale, it won’t launch the next big startup, and it’s usually not breaking new technical ground – but it is useful. I can see how I’m making somebody’s life better, and isn’t that what computers are for? If other people like it, that’s a nice bonus, but I’m really thinking about that one person. Normally the one person I’m writing software for is me, so it’s extra nice when I can do it for somebody else. If you want to try this tool yourself, go to alexwlchan.net/my-tools/add-cover-to-ao3-epubs/ If you want to read the code, it’s all on GitHub. [If the formatting of this post looks odd in your feed reader, visit the original article]

7 hours ago 3 votes
Non-alcoholic apéritifs

I’ve been doing Dry January this year. One thing I missed was something for apéro hour, a beverage to mark the start of the evening. Something complex and maybe bitter, not like a drink you’d have with lunch. I found some good options. Ghia sodas are my favorite. Ghia is an NA apéritif based on grape juice but with enough bitterness (gentian) and sourness (yuzu) to be interesting. You can buy a bottle and mix it with soda yourself but I like the little cans with extra flavoring. The Ginger and the Sumac & Chili are both great. Another thing I like are low-sugar fancy soda pops. Not diet drinks, they still have a little sugar, but typically 50 calories a can. De La Calle Tepache is my favorite. Fermented pineapple is delicious and they have some fun flavors. Culture Pop is also good. A friend gave me the Zero book, a drinks cookbook from the fancy restaurant Alinea. This book is a little aspirational but the recipes are doable, it’s just a lot of labor. Very fancy high end drink mixing, really beautiful flavor ideas. The only thing I made was their gin substitute (mostly junipers extracted in glycerin) and it was too sweet for me. Need to find the right use for it, a martini definitely ain’t it. An easier homemade drink is this Nonalcoholic Dirty Lemon Tonic. It’s basically a lemonade heavily flavored with salted preserved lemons, then mixed with tonic. I love the complexity and freshness of this drink and enjoy it on its own merits. Finally, non-alcoholic beer has gotten a lot better in the last few years thanks to manufacturing innovations. I’ve been enjoying NA Black Butte Porter, Stella Artois 0.0, Heineken 0.0. They basically all taste just like their alcoholic uncles, no compromise. One thing to note about non-alcoholic substitutes is they are not cheap. They’ve become a big high end business. Expect to pay the same for an NA drink as one with alcohol even though they aren’t taxed nearly as much.

2 days ago 5 votes
It burns

The first time we had to evacuate Malibu this season was during the Franklin fire in early December. We went to bed with our bags packed, thinking they'd probably get it under control. But by 2am, the roaring blades of fire choppers shaking the house got us up. As we sped down the canyon towards Pacific Coast Highway (PCH), the fire had reached the ridge across from ours, and flames were blazing large out the car windows. It felt like we had left the evacuation a little too late, but they eventually did get Franklin under control before it reached us. Humans have a strange relationship with risk and disasters. We're so prone to wishful thinking and bad pattern matching. I remember people being shocked when the flames jumped the PCH during the Woolsey fire in 2017. IT HAD NEVER DONE THAT! So several friends of ours had to suddenly escape a nightmare scenario, driving through burning streets, in heavy smoke, with literally their lives on the line. Because the past had failed to predict the future. I feel into that same trap for a moment with the dramatic proclamations of wind and fire weather in the days leading up to January 7. Warning after warning of "extremely dangerous, life-threatening wind" coming from the City of Malibu, and that overly-bureaucratic-but-still-ominous "Particularly Dangerous Situation" designation. Because, really, how much worse could it be? Turns out, a lot. It was a little before noon on the 7th when we first saw the big plumes of smoke rise from the Palisades fire. And immediately the pattern matching ran astray. Oh, it's probably just like Franklin. It's not big yet, they'll get it out. They usually do. Well, they didn't. By the late afternoon, we had once more packed our bags, and by then it was also clear that things actually were different this time. Different worse. Different enough that even Santa Monica didn't feel like it was assured to be safe. So we headed far North, to be sure that we wouldn't have to evacuate again. Turned out to be a good move. Because by now, into the evening, few people in the connected world hadn't started to see the catastrophic images emerging from the Palisades and Eaton fires. Well over 10,000 houses would ultimately burn. Entire neighborhoods leveled. Pictures that could be mistaken for World War II. Utter and complete destruction. By the night of the 7th, the fire reached our canyon, and it tore through the chaparral and brush that'd been building since the last big fire that area saw in 1993. Out of some 150 houses in our immediate vicinity, nearly a hundred burned to the ground. Including the first house we moved to in Malibu back in 2009. But thankfully not ours. That's of course a huge relief. This was and is our Malibu Dream House. The site of that gorgeous home office I'm so fond to share views from. Our home. But a house left standing in a disaster zone is still a disaster. The flames reached all the way up to the base of our construction, incinerated much of our landscaping, and devoured the power poles around it to dysfunction. We have burnt-out buildings every which way the eye looks. The national guard is still stationed at road blocks on the access roads. Utility workers are tearing down the entire power grid to rebuild it from scratch. It's going to be a long time before this is comfortably habitable again. So we left. That in itself feels like defeat. There's an urge to stay put, and to help, in whatever helpless ways you can. But with three school-age children who've already missed over a months worth of learning from power outages, fire threats, actual fires, and now mudslide dangers, it was time to go. None of this came as a surprise, mind you. After Woolsey in 2017, Malibu life always felt like living on borrowed time to us. We knew it, even accepted it. Beautiful enough to be worth the risk, we said.  But even if it wasn't a surprise, it's still a shock. The sheer devastation, especially in the Palisades, went far beyond our normal range of comprehension. Bounded, as it always is, by past experiences. Thus, we find ourselves back in Copenhagen. A safe haven for calamities of all sorts. We lived here for three years during the pandemic, so it just made sense to use it for refuge once more. The kids' old international school accepted them right back in, and past friendships were quickly rebooted. I don't know how long it's going to be this time. And that's an odd feeling to have, just as America has been turning a corner, and just as the optimism is back in so many areas. Of the twenty years I've spent in America, this feels like the most exciting time to be part of the exceptionalism that the US of A offers. And of course we still are. I'll still be in the US all the time on both business, racing, and family trips. But it won't be exclusively so for a while, and it won't be from our Malibu Dream House. And that burns.

2 days ago 6 votes
Slow, flaky, and failing

Thou shalt not suffer a flaky test to live, because it’s annoying, counterproductive, and dangerous: one day it might fail for real, and you won’t notice. Here’s what to do.

3 days ago 7 votes
Name that Ware, January 2025

The ware for January 2025 is shown below. Thanks to brimdavis for contributing this ware! …back in the day when you would get wares that had “blue wires” in them… One thing I wonder about this ware is…where are the ROMs? Perhaps I’ll find out soon! Happy year of the snake!

3 days ago 5 votes