One year of Pinafore

Screenshot of Pinafore showing a compose input

Pinafore is a standalone web client for Mastodon, which recently hit version 1.9.0. Here are some notable new features:

It’s been about a year since I first launched Pinafore. So I’d like to reflect on where the project came from, and where I hope to take it.

Background

In 2017, I was in a funk. I had stopped contributing to the PouchDB project largely due to burnout, and for various reasons I eventually left my job at Microsoft. In the meantime, I had become enamored of Mastodon and even contributed to it, but I was feeling restless and looking for a new project.

The Mastodon codebase is extremely well-written. I’m convinced that Eugen Rochko is some kind of savant. However, I never took much of a liking to React, and I found it difficult to fix some fundamental problems in the Mastodon UI, such as offline support or the occasionally jerky scrolling. I also really missed the single-column layout of Twitter (I was never a Tweetdeck fan).

So the idea came to me to create my own Mastodon web client. I had been working on web sites for years, but aside from some small prototypes, I had never built an entire web app by myself. This was an opportunity to test some of my ideas about how a web app “should” be, leveraging my experience in web performance and standards. Also, I wanted to teach myself about accessibility, which I had never really studied before.

I knew I wanted to use Svelte, because I agreed with Rich Harris and Tom Dale that JavaScript frameworks should focus less on being runtime APIs and more on being compilers. Incidentally, I was at the same talk by Jed Schmitt that Rich mentions in this video, and it blew my mind as much as it blew his. (The difference between Rich and me is that he actually went off and built a whole framework based on it!)

I started working on Pinafore at the end of December 2017, and released it in April 2018. So after 18 months of development, I’d like to consider where Pinafore has done well and where it can improve.

Success metrics

Pinafore doesn’t have any trackers on it, so I don’t know how many people are using it. Sure, I could use a privacy-respecting tracker like Fathom, but the Mastodon community is pretty allergic to any kind of tracking, so I’ve been hesitant to add it. In any case, I don’t really care, because I would work on Pinafore regardless of how many people are using it.

However, I do get a trickle of questions and bug reports about Pinafore, and the #Pinafore hashtag is pretty active. I’ve also heard from several folks that it’s their preferred Mastodon interface. The reasons they give are usually one of the following:

  • Accessibility: I’ve focused a lot on making Pinafore work well with keyboard navigation and screen readers. (Marco Zehe‘s guidance really helped!)
  • Design: the single-column layout of Pinafore is a key differentiator with the Mastodon frontend (although not for long).
  • Instance-switching: people who juggle multiple accounts on different instances don’t necessarily want one browser tab for each.

My favorite user testimonial, though, is from my wife. She told me, “I like Pinafore because it never loses my place in the timeline.” (Much of my motivation for working on Pinafore can be credited to “wife-driven development” – I like making her happy!)

So this confirms that I’ve achieved at least some of the goals from the Pinafore introductory blog post. Although notably, offline support is rarely mentioned, but I’ll get to that later.

Collaboration

Pinafore has also benefited from a lot of community contributions. I’d like to specifically thank:

And of course everyone else who contributed. Thank you so much!

There are some challenges with building a dev community around Pinafore. The app is implemented using Svelte v2 and Sapper, which unfortunately causes two downsides in terms of onboarding: 1) Svelte isn’t a very well-known framework, and 2) Svelte v2 is incompatible with Svelte v3, and there’s no upgrade path currently.

I’ll have to continue grappling with these challenges, but for now I’m very satisfied with Svelte v2. It’s fast, lightweight, and does everything I need it to. So I’m not in a big hurry to upgrade.

And oh yeah: Svelte really is lightweight. Pinafore only loads 32KB of compressed JavaScript for the landing page, and 137KB for the Home timeline. The total size of all JS assets is under 300KB compressed (<1MB raw). It gets a perfect 100 score from Lighthouse.

Screenshot of Lighthouse showing perfect 100 score in all categories, including Performance, Accessibility, Best Practices, and SEO

If you didn’t think I was going to brag about web perf vanity metrics, then you don’t know me very well.

Future plans

My first goal with Pinafore is completeness. Even though I’ve been working on it for over a year, there are still plenty of missing features compared to the Mastodon frontend. And although the gap has been narrowing, Mastodon itself hasn’t stopped innovating, so there’s always new stuff to add. (Polls! Blurhash! Keybase! Does Eugen ever sleep?)

Beyond that, I’d like to start focusing on features that make Pinafore a more pleasant social media experience. One of the virtues of decentralized social media is that we can experiment with features that give people control over their social media experience, even if it hampers addictiveness or growth. To that end, I’ve added a set of wellness features, inspired by Tristan Harris’s Center for Humane Technology. I’ll probably tweak and expand these features as feedback rolls in.

I’d also like to improve offline support. Even though Pinafore does have an offline mode, and even though it uses a Service Worker to cache static assets, it’s not very offline-first. Instead, it uses offline storage more as a fallback for when the network fails, rather than as the primary source of truth.

Given my background working on offline-first technology and advocating for it, I find this a bit disappointing. But it turns out that it’s really difficult to implement an offline-first social media UI. How do you deal with offline writes? How do you handle the gap between fresh content and stale content within the same timeline? These are not easy questions, and for the most part I’ve punted on them. But Pinafore can do better.

Conclusion

Pinafore is a passion project for me. It gives me something interesting to do on weekends and evenings, and it teaches me a lot about how the web platform works.

I also see Pinafore as an opportunity to provide more options to the Mastodon community, and to prove that you don’t have to treat Eugen as a gatekeeper for every minor UI tweak you’d like to see in Mastodon. Mastodon is decentralized; let’s decentralize the interface!

I have every intention to keep working on Pinafore, and I’m curious to know where you think it should go next.

Tech veganism

Hand draw picture of a computer with vegetables on the screen

A year ago I wrote a blog post about living with an open-source phone. A commenter on Reddit described this as “tech veganism,” and I thought it was a great metaphor.

For the past few years I’ve swum in a lot of “tech veganism” circles (mostly thanks to Mastodon), so I think I have a good definition now:

  • a preference for open-source software over proprietary software
  • a suspicion of big tech companies
  • a high bar for privacy and security

The parallel with veganism is a good one for several reasons. First off, a lot of people find vegans annoying. “What, you think you’re better than me?” It’s a lot easier to eat animal products if you don’t think about where they come from, and vegans are an uncomfortable reminder of food’s icky origins. Plus, it’s never pleasant to think that someone might be silently judging you for your personal lifestyle choices.

Now imagine someone telling you they don’t use Google, Facebook, etc. “What, you think you’re better than me?”

Second, people don’t typically choose tech veganism because it’s a better user experience. Even though some vegans may swear that their black bean burger tastes just as good as your Angus beef, the honest ones will acknowledge that it’s more about principle than palate.

Similarly, there are plenty of tech vegans who will claim that OpenStreetMap is a great replacement for Google Maps, or that DuckDuckGo provides better search results than Google. But if they’re being honest with themselves, they’ll admit that they’re more motivated by principle than quality or convenience.

Third, tech veganism is a good way to alienate people. Talk about it enough, and you may get accused of being overly dour, negative, cynical, etc. Can’t you just eat a burger and enjoy it like a normal person? Why do you have to bring up factory farms all the time, and ruin my meal? Similarly: why can’t you just use Google like a normal person? Why do you have to drone on and on about LibreOffice and OpenBSD?

Tech veganism can even cost you friends. In the same way that having one vegan in the group severely limits your restaurant choices, being the one tech vegan among your friends can really narrow down the options for communication apps. Sure, you can ask them to use Signal. Or email. But most likely, the group chat will just happen without you, and you won’t be getting any Facebook invites.

Fourth, in some cases tech veganism is difficult if not downright impossible. If you’ve ever actually tried to go vegan (note: I have), you’ll find it’s a constant battle of reading ingredient lists to find the hidden references to milk, eggs, fish, etc. (Some surprising things that aren’t vegan: Worcestershire sauce, kimchi, even sugar.) And once you travel to a foreign country, you might find yourself surviving on bread and water, or else giving up entirely and grabbing a croque-monsieur.

Similarly, it’s almost impossible to avoid proprietary software or the tech giants. Consider this FOSDEM talk, where the executive director of the Software Freedom Conservancy admits to having proprietary software embedded inside her body, because that was the only option for a defibrillator. She advocates open-source software, and yet despite her best efforts, she’s a closed-source cyborg!

Try to avoid Google or Amazon and you may find yourself in a similar boat. There’s a great series of posts from Kashmir Hill where she shows that it’s nearly impossible to quit the tech giants because of how enmeshed they are in every app, website, and network request. It’s easier to find something vegan at a Brazilian barbecue than it is to eliminate big tech from your internet diet.

Another similarity: just as there are more vegetarians in India, tech veganism can be surprisingly region-specific. In particular, Europeans have more reason than Americans to embrace tech veganism, because the non-Chinese tech giants – Google, Apple, Facebook, Amazon, Microsoft, the dreaded GAFAM – are all American.

The dominance of American tech platforms in Europe, and especially all the data from European citizens being siphoned back to Silicon Valley, can feel like an issue of national sovereignty. Hence we have France opting for the open-source Matrix project for government communications, or organizations like Framasoft depicting themselves as a tiny Astérix-like village holding off foreign invaders. And the invaders really are foreign!

One place where the veganism metaphor breaks down is that, although nearly anyone can be a vegan, tech veganism is mostly practiced by those who are expert enough or privileged enough to learn the elaborate workarounds to avoid the GAFAMs of the world. Setting up an Ubuntu laptop, a LineageOS phone, a Fastmail account, and wiring it all together so that you actually get calendar notifications is no easy feat. You will probably have to get your hands dirty on the command line.

I find that there’s a bit of a “let them eat cake” attitude among tech vegan boosters, because they often discount the sheer difficulty of all this stuff. (“Let them use Linux” could be a fitting refrain.) After all, they figured it out, so why can’t you? What, doesn’t everyone have a computer science degree and six years experience as a sysadmin?

To be a vegan, all you have to do is stop eating animal products. To be a tech vegan, you have to join an elite guild of tech wizards and master their secret arts. And even then, you’re probably sneaking a forbidden bite of Google or Apple every now and then.

So where does that leave tech veganism? Well, for the near term, it’s probably going to remain the province of geeks and specialists due to the difficulties described above. Ironically, that means that it’ll mostly involve tech workers building products that other tech workers refuse to use. (Imagine if veganism were only practiced by employees of meat companies.)

I also suspect that tech veganism will begin to shift, if it hasn’t already. I think the focus will become less about open source vs closed source (the battle of the last decade) and more about digital well-being, especially in regards to privacy, addiction, and safety. So in this way, it may be less about switching from Windows to Linux and more about switching from Android to iOS, or from Facebook to more private channels like Discord and WhatsApp.

Generation Z has grown up with smartphones and app stores as an inescapable fact of their lives. Does anyone under 21 actually care whether the code on their phone is open-source and whether, Stallman-style, they can dive into an Objective-C file and change something? Probably not many. Does anyone in that demographic care about their phone’s impact on their anxiety, their quality time with friends and family, and their safety from harassment and abuse? Probably a lot more.

In my opinion, this change is a good thing. You shouldn’t have to enter an elite tech priesthood just to preserve your privacy, security, and safety online. Tech veganism should be as easy as regular veganism – it should just be an option on the menu. That doesn’t mean that it won’t suffer from many of the same problems as regular veganism, but at least it will be democratized.

Get off of Twitter

Twitter logo with a red "no" sign over it

Stop complaining about Twitter on Twitter. Deny them your attention, your time, and your data. Get off of Twitter.

The more time you spend on Twitter, the more money you make for Twitter. Get off of Twitter.

You at-mention @jack and call him out for the harassment and disinformation on his platform. You get a few hundred likes and retweets, each one sending your brain a little boost of serotonin. Twitter learns that you are interested in people who criticize @jack and starts to recommend you their tweets. You end up spending more time on Twitter, and advertisers learn a little bit more about you. You make @jack more money.

Get off of Twitter.

You can’t criticize Twitter on Twitter. It just doesn’t work. The medium is the message.

There’s an old joke where one fish says to the other, “How’s the water today?” And the fish responds, “What’s water?” On Twitter, you might ask, “How’s the outrage today?” (The answer, of course, is “I hate it! I’m so outraged about it!”)

Get off of Twitter.

Write blog posts. Use RSS. Use micro.blog. Use Mastodon. Use Pleroma. Use whatever you want, as long as it isn’t manipulating you with algorithms or selling access to your data to advertisers.

You’re worried about losing your influence. How about using your influence for something good? How about using it to stick it to Twitter, if you really dislike Twitter so much? Maybe if you do it, and your friends do it, then it will cause a sea change. After all, who was ever “influential” by following the crowd?

As Gandhi said (in paraphrase), “Be the change you want to see in the world.” Or as another influencer put it: “I’m starting with the man in the mirror.” Or if you prefer: “Practice what you preach.”

Get off of Twitter.

In defense of the Right Thing

It has come to my attention that many people believe the Wrong Thing. I find this to be an intolerable state of affairs, so this is a blog post defending what is Right.

How do I know there are so many people who believe the Wrong Thing? Well because, like everyone, I use Twitter. And holy moly! My feed is chock-full of Wrong Thinkers.

Sometimes it feels like everyone in the world believes the Wrong Thing, and I’m the last lonely person clinging to what’s Right. There must be a global epidemic of Wrongness. Why else would Twitter fill my feed with so many of these dunces and ninnies and halfwits?

I don’t even follow these people. Why should they be in my timeline, unless the whole world is full of Wrong people?

Every time I see their Wrong tweets, I seethe with rage and eagerly click to read the full thread. I might spend hours this way, thumbing through Wrong tweets. “How can so many people be so Wrong?” I’ll say to myself, shaking my head as I continue to scroll.

Sometimes when I find a really Wrong tweet, I’ll quote it and tweet it out with the perfect devastating repartee. That way, more people who agree with me are exposed to these Wrong views. That’ll teach ’em!

I do have to commend the others who proudly rise in defense of what is Right. On Twitter, I often see them caught in an epic battle with the Wrongers – “34 people are talking about this!” Well, here comes a 35th, joining the fray to fight the good fight.

To be honest, I sometimes get tired of feeling angry all the time. But how can I not be, when the world is full of people who are so very Wrong?

Curiously, the Wrongers seem to come from all sides of any issue, and they are legion. People who use margarine instead of butter, people who peel the banana from the top instead of the bottom, people who crack a boiled egg from the big end rather than the small end. These are exactly the things that drive me nuts, and somehow my feed is full of people who believe the opposite of me on precisely those issues! Sometimes I feel utterly embroiled, helpless, a tiny voice of reason shouting against an angry mob of Wrong Thinkers.

But sadly, this is just how the world is these days. The world is full of people arguing, calling each other out, or watching a fight unfold with the horrified glee of a driver craning their neck to get a good look at a car wreck.

I know this is how the world is, because I see it on Twitter. And Twitter is an utterly unbiased mirror of the world, with no algorithms that subtly push the discussion in one direction or the other, regardless of whether it is good for discourse or compassion or human well-being but only whether it is good for Twitter.

2018 book review

In the spirit of last year’s blog post, this is an extremely belated round-up of books I read in 2018.

I read a lot of books last year. I chalk this up to many things, but quitting Twitter was probably a big one. Without Twitter, I actually run out of things to read on the internet. At some point the internet gets boring, and it’s nice to have a few books around to pick up the slack.

A photo of books on a bookshelf

A completely unstaged photo of my remarkably tidy bookshelf

To keep things simple, I’ll focus on the books I actually enjoyed, rather than the ones I thought were duds. Who wants to read about boring books, anyway?

So without further ado:

Quick links

Fiction

Non-fiction

Fiction

Life’s too short not to enjoy good fiction. I don’t read it often enough, but when I do find a good novel, it’s like a breath of fresh air. I can immerse myself in another world for a while, and try to empathize with its characters.

This year I didn’t read a lot of fiction, but what I did read was very good.

Theory of Bastards by Audrey Schulman (2018)

What a remarkable book. I read it because I got a sudden hankering for post-apocalyptic fiction last year, plus there was a review in The Economist.

This book definitely doesn’t disappoint on the post-apocalyptic front, but it’s also just a great piece of pop-science drama. It’s clear that the author did their research (about bonobos in this case) and developed the characters with a lot of care. Strongly recommended.

Three Weeks in December by Audrey Schulman (2012)

This book is a bit harder to find, but I found it’s every bit as good as Schulman’s other book. Some well-researched science, a bit of colonial history, and once again Schulman’s keen eye for character development (even if her protagonist bears a resemblance to the one from Bastards). I may just have to read everything Schulman’s ever written.

Blindness by José Saramago (1995)

Continuing the post-apocalyptic trend, this is a tightly-written drama that captures what’s best about science fiction: an understanding of how humans react when put into unfamiliar situations, for good and for evil. Also it’s written in one of the most unique styles I’ve ever read – breathless, using mostly commas, with hardly a period to stop the flow of action.

Apparently there’s a movie, but the reviews were bad so I didn’t watch it. The book is great.

A Canticle for Leibowitz by Walter M. Miller Jr. (1959)

I promise I didn’t just read post-apocalyptic fiction this year. In any case, this book is considered a classic, but I found it to be three thinly-connected novellas where only the first one is worth reading. If you get bored during the second one, I’d say skip it.

What’s great about this book is its understanding for how humans behave during a dark age. Knowledge gets lost or deliberately destroyed, history becomes myth, cult becomes religion. It’s chilling to imagine how that may happen to our own civilization.

Never Let Me Go by Kazuo Ishiguro (2010)

Last post-apocalyptic fiction, I promise. This one is pretty good, although you’re better off not reading any reviews or synopses so as to not spoil the premise.

Also, if you’re expecting something like The Remains of the Day, then you should know that Never Let Me Go is not only very different thematically, but I also found it didn’t linger in my mind as much as Remains did. Still, it’s worth reading.

Once again, there’s a movie, but I haven’t seen it. I have no idea how you would adapt a book like this into a movie, but I guess that doesn’t stop people from trying.

A Tale for the Time Being by Ruth Ozeki (2013)

Marvelous book. Maybe as someone who’s studied French and Japanese, and who lives in the Pacific Northwest, and who has developed a middling interest in Buddhism, the book also seemed to speak to me in a eerily precise way.

If you’re a fan of wordplay, inventive storytelling, and Murakami-style magic realism, this is a great book to pick up.

Cherry by Nico Walker (2018)

A strange, haunting, disturbing book, but also a page-turner. Apparently this is Walker’s first book, and it’s really a virtuoso performance. I’ll be interested to see what he comes up with next.

The New York Trilogy by Paul Auster (1987)

A bizarre book. You get the impression that Paul Auster had one story to tell, but he couldn’t decide how to do it, so he wrote the same story three ways. Somewhat like Canticle, you’re probably better off just reading the first one and skipping the others.

Still, it’s worth it, especially if you’re a fan of hard-boiled detective fiction and surrealism.

Non-fiction

As usual for me, I read a lot of non-fiction last year. For the purposes of this list, though, I’m skipping about half the books I read, because a lot of them didn’t have much new to say, or didn’t really stick in my mind.

So this list contains only books I would recommend. (Note: that doesn’t mean I agree with every sentence that the author has ever uttered. Which should be obvious, but hey, on the internet, you’re rarely given the benefit of the doubt these days.)

Sapiens (2014), Homo Deus (2017), and 21 Lessons for the 21st Century (2018) by Yuval Noah Harari

I first came across Harari in this excellent Atlantic article, and I quickly devoured all three of his books. I’m not really sure what to say, except that he’s one of the most clever, funny, and innovative thinkers writing today. Sapiens in particular is a masterpiece of history, anthropology, and political science.

Harari has upset my thinking on a lot of things – in particular, how technology interacts with democracy and citizenship to potentially upend the relationship between the individual and the state in the 21st century. I also found his portrayal of factory farming as one of the biggest moral failures in modern human history to be very compelling (and not just because I’ve been an on-again off-again pescetarian for years).

But probably the best thing I can say about these books is that they’re all page-turners, despite the dense subject matter. Also, Harari isn’t afraid to veer into controversy, but he’s always clear about where he gets the facts that inform his opinions. (His conclusion in Sapiens that, scientifically, we just don’t know why humans are a patriarchal species was refreshingly honest.)

Some of the best non-fiction I’ve read in my life, and definitely worth picking up.

The Mechanical Horse by Margaret Guroff (2016)

A joyous book, a celebration of the bicycle and its impact on American society. Even if you’re not a cyclist, you may find this book interesting, just to see how much a simple two-wheeled vehicle has reshaped our culture.

If you want a preview, you can take a look at this excerpt on how bicycles did, in Susan B. Anthony’s words, “more to emancipate women than any one thing in the world.”

Enlightenment Now by Steven Pinker (2018)

Everyone’s already reviewed, rebutted, and counter-rebutted this book to death during 2018, but I’ll toss my hat in the ring as well.

This is maybe the most important book I read last year.

In terms of non-fiction, I’ve spent the last few years mostly reading what my wife calls “tech is destroying the world” books. So my non-fiction diet is generally very dire and gloomy and focused on all the problems in the world (and, as a techie, how I’m partially complicit in it). Maybe it’s no surprise then that I tend to be pretty pessimistic in my outlook.

This book aims to destroy that mindset with cold-hard data. And the thing is, I can’t argue with most of his points. The world really has gotten better over the past few centuries, unless you want to refute dozens of meticulously-researched charts and graphs.

I’ve read some of the rebuttals, and most of them pick one or two points and try to skewer them to death, as if that invalidates the entire book. But the book is over 400 pages; you can’t take exception with one or two pages and then pretend that it invalidates the main sweep of his argument.

I still retain some skepticism that mankind can effectively tackle climate change, the transition away from fossil fuels, the drug epidemic (Pinker’s one graph that doesn’t slope in the good direction), or the decline of liberal values, but Pinker gives about a hundred reasons for optimism. So for that, I’m grateful.

Why Liberalism Failed by Patrick Deneen (2018)

Now here’s a truly radical book, and one that might serve as a good contrast to Pinker’s. Deneen’s argument is basically that liberalism (in the sense of “Western liberalism,” or “the Washington consensus,” or whatever you want to call it) has become a victim of its own success. It works so well to elevate the individual – as a consumer, as a liberated sexual being, etc. – that it rips apart families and communities in the process.

Deneen might look at Pinker’s book and say, “Okay, so we’ve got all the material comfort we could possibly want, but maybe the human soul needs something more than that?”

One thought that this book helped crystallize in my head, along with Identity by Francis Fukuyama (which I read and thought was just okay) and Why Buddhism is True by Robert Wright (which I started but didn’t finish) is that there’s a lot of wisdom to the Buddhist idea that suffering is caused by desire, and you can’t really address your suffering without addressing your desire.

Here’s Deneen:

“[H]uman appetite is insatiable and the world is limited. For both of these reasons, we cannot be truly free in the modern sense. We can never attain satiation, and will be eternally driven by our desires rather than satisfied by their attainment. And in pursuit of the satisfaction of our limitless desires, we will very quickly exhaust the planet.” (p125, hardcover)

Some of these arguments you may even call environmentalist. Others, like his critique of free markets, you might call anticapitalist. Despite broadly coming across as something like “radical reactionary paleo-conservative,” Deneen is hard to pin down.

I can’t really do this book justice by summarizing it here, as Deneen is such an original thinker that it’s best to let him explain his arguments himself. I don’t agree with him on everything, but I think if you want to understand the recent decline in liberal values, this book (as well as Edward Luce’s The Retreat of Western Liberalism, which I reviewed last year) would be a good place to start.

Thinking, Fast and Slow by Daniel Kahneman (2011)

This might be the second most important book I read last year (after Pinker’s). It’s a good overview of all the ways that our brains can fool us, and how statistical thinking can help reshape your perception of the world.

One part that stood out to me was the story of the Israeli flight instructor who would always criticize his students after a bad flight, and praise them after a good flight. And since a bad flight is usually followed by a better one, but a good flight is usually followed by a worse one, he concluded that criticism works, but praise doesn’t.

Kahneman points out that this is really just an expression of regression to the mean. If he had been berating his students on how high of a dice roll they got (“You got a six, stupendous!” or “You got a one, try harder!”) he would get exactly the same effect. The widespread belief in things like “streaks” and “hot hands,” though, shows how far most folks are from internalizing this basic statistical concept.

I’m sure this book hasn’t totally cured me of my monkey-brain misconceptions, but hopefully I’ll be able to catch a few more of them now.

Ten Arguments for Deleting Your Social Media Accounts Right Now by Jaron Lanier (2018)

Despite the snippy title, this is a well-thought-out and sobering book that puts a lot of recent social media controversies into perspective. I like how Lanier draws a line from Twitter and Facebook all the way back to old-school message boards.

This book has made me more skeptical of social media in general (even after deleting my Twitter account). If nothing else, reading this book is a good way to reflect on one own’s social media usage, and whether or not it’s a healthy relationship.

“Briefly I was one of the HuffPost’s top bloggers, always on the front page. But I found myself falling into that old problem again whenever I read the comments, and I could not get myself to ignore them. I would feel this weird low-level boiling rage inside me. Or I’d feel this absurd glow when people liked what I wrote, even if what they said didn’t indicate that they had paid much attention to it. Comment authors were mostly seeking attention for themselves.

We were all in the same stew, manipulating each other, inflating ourselves. (pp42-43, hardcover)

Atrocities by Matthew White (2011)

The funniest book about war, genocide, and famine that you’ll ever read. If it doesn’t sound like “funniest” belongs in the same sentence with those words, it’s best to pick up the book yourself to understand what I mean.

This book is not only well-researched and educational; it’s also a masterpiece of black humor that will make you gape and shake your head at the depths of human stupidity.

On Genghis Khan:

“Keep in mind that even with 16 million [living] descendants, Genghis Khan hasn’t replaced the number of people he killed.” (p115, 2013 edition)

On the Time of Troubles:

  • Location: Russia
  • Number of Dmitris: 4
  • Lesson learned: Always insist on seeing a photo ID before you proclaim someone emperor. (p207, 2013 edition)

There’s more, but I’d hate to spoil it by quoting it at length. In any case, it’s a hard book to put down, and a good conversation-starter to leave on a coffee table.

The Stranger in the Woods by Michael Finkel (2017)

A “strange but true” story about a guy who lived alone in the woods for 27 years. Pretty fascinating story, and a good meditation on what it means to live with other people, and why some might try to escape it.

The Blank Slate by Steven Pinker (2002)

I was intrigued by Pinker’s Enlightenment Now, so I picked this one up. It’s a bit older, but its core argument is still relevant. It does a great job of unmasking a kind of lazy thinking that denies any evidence from biology that might be politically inconvenient. (On both the left and the right, but I think Pinker focuses on the left because that’s his target audience.)

Something this book helped me realize is that, if I have a core political belief, it’s that evidence and facts matter. Unfortunately there’s a growing strain of thought across the political spectrum that emphasizes lived experiences, feelings, and political expediency at the expense of evidence and a shared set of facts. It argues that science is a tool of oppression, and “facts” are only useful for information warfare.

I can appreciate the anti-elitism and distrust of authority that motivates a lot of this thinking, but I also think it’s inherently dangerous. Science, reason, and liberalism have lifted a large chunk of humanity out of poverty and misery, and we abandon them at our own risk.

The Curse of Bigness by Tim Wu (2018)

In a year conspicuously absent of “tech is destroying the world” books on my bookshelf (or at least, on this list), this one stands out. It’s short, it’s sweet, and it captures our present tech moment very well by identifying the main problem as being a political one, rather than an economic or a technical one.

Essentially, this is a history of antitrust and anti-antitrust thinking. Wu’s other books (The Master Switch and The Attention Merchants) did a great job of tying together present trends with a history lesson, and this book offers more of the same. Well worth a read.

Living with Tinnitus by Laura Cole (2017)

This is a very personal inclusion on this list, but I’ll put it here anyway.

Last year I developed tinnitus, which means that I constantly hear a low, high-pitched buzzing in my ears. It affects a large number of people (10-15% of the population according to Wikipedia), but surprisingly few talk about it.

How did I get it? Probably from listening to too many podcasts on the bus without noise-canceling headphones. Or maybe an ear infection. To be honest, I’m not sure.

When it first developed, it was scary and frustrating. I went to an audiologist, but they just ran some tests, told me my hearing was fine, and sent me on my way with a short pamphlet on tinnitus. For several months it affected my sleep, my mood, and my social life, but I wasn’t really sure what to do about it.

Almost a year later, I can say that my tinnitus doesn’t really bother me at all, and I rarely think about it, even though it’s the first thing I hear in the morning and the last thing I hear at night. This book was one of the things that helped with that.

As it turns out, tinnitus doesn’t have a lot of research. Reading internet forums can just make you more scared, as the symptoms and severity can vary wildly. This book acknowledges the lack of evidence, but offers a wide range of potential solutions, mitigations, and coping mechanisms. The author is also very clear about which ones have strong evidence and which ones don’t.

If you or one of your loved ones has tinnitus, I’d strongly recommend this book.

Building a modern carousel with CSS scroll snap, smooth scrolling, and pinch-zoom

Recently I had some fun implementing an image carousel for Pinafore. The requirements were pretty simple: users should be able to swipe horizontally through up to 4 images, and also pinch-zoom to get a closer look.

The finished product looks like this:

 

Often when you’re building something like this, it’s tempting to use an off-the-shelf solution. The problem is that this often adds a large dependency size, or the code is inflexible, or it’s framework-specific (React, Vue, etc.), or it may not be optimized for performance and accessibility.

Come on, it’s 2019. Isn’t there a decent way to build a carousel with native browser APIs?

As it turns out, there is. My carousel implementation uses a few simple building blocks:

  1. CSS scroll snap
  2. scrollTo() with smooth behavior
  3. The <pinch-zoom> custom element

CSS scroll snap

Let’s start off with CSS scroll snap. This is what makes the scrollable element “snap” to a certain position as you scroll it.

The browser support is pretty good. The only trick is that you have to write one implementation for the modern scroll snap API (supported by Chrome and Safari), and another for the older scroll snap points API (supported by Firefox[1]).

You can detect support using @supports (scroll-snap-align: start). As usual for iOS Safari, you’ll also need to add -webkit-overflow-scrolling: touch to make the element scrollable.

But lo and behold, we now have the world’s simplest carousel implementation. It doesn’t even require JavaScript – just HTML and CSS!

Note: for best results, you may want to view the above pen in full mode.

The benefit of having all this “snapping” logic inside of CSS rather than JavaScript is that the browser is doing the heavy lifting. We don’t have to use touchmove listeners or requestAnimationFrame to try to get the pixel-perfect snapping behavior with the right animation curve – the browser handles all of it for us, in native code.

And unlike touchmove, this scroll-snapping works for any method of scrolling – touchpad, touchscreen, scrollbar, you name it.

scrollTo() with smooth scrolling

The next piece of the puzzle is that most carousels have little indicator buttons that let you navigate between the items in the list.

Screenshot of a carousel containing an image of a cat with indicator buttons below showing 1 filled circle and 3 unfilled circles

For this, we will need a little bit of JavaScript. We can use the scrollTo() API with {behavior: 'smooth'}, which tells the browser to smoothly scroll to a given offset:

function scrollToItem(itemPosition, numItems, scroller) {
  scroller.scrollTo({
    scrollLeft: Math.floor(
      scroller.scrollWidth * (itemPosition / numItems)
    ),
    behavior: 'smooth'
  })
}

The only trick here is that Safari doesn’t support smooth scroll behavior and Edge doesn’t support scrollTo() at all. But we can detect support and fall back to a JavaScript implementation, such as this one.

Here is my technique for detecting native smooth scrolling:

function testSupportsSmoothScroll () {
  var supports = false
  try {
    var div = document.createElement('div')
    div.scrollTo({
      top: 0,
      get behavior () {
        supports = true
        return 'smooth'
      }
    })
  } catch (err) {}
  return supports
}

Being careful to set aria-labels and aria-pressed states for the buttons, and adding a debounced scroll listener to update the pressed state as the user scrolls, we end up with something like this:

View in full mode

You can also add generic “go left” and “go right” buttons; the principle is the same.

Hiding the scrollbar (optional)

Now, the next piece of the puzzle is that most carousels don’t have a scrollbar, and depending on the browser and OS, you might not like how the scrollbar appears.

Also, our carousel already includes all the buttons needed to scroll left and right, so it effectively has its own scrollbar. So we can consider removing the native one.

To accomplish this, we can start with overflow-x: auto rather than overflow-x: scroll, which ensures that at least if there’s only one image (and thus no possibility of scrolling left or right), the scrollbar doesn’t show.

Beyond that, we may be tempted to add overflow-x: hidden, but this actually makes the list entirely unscrollable. Bummer.

So we can use a little hack instead. Here is some CSS to remove the scrollbar, which works in Chrome, Edge, Firefox, and Safari:

.scroll {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.scroll::-webkit-scrollbar {
  display: none;
}

And it works! The scrollbar is gone:

View in full mode

Admittedly, though, this is a bit icky. The only standards-based CSS here is scrollbar-width, which is currently only supported by Firefox. The -webkit-scrollbar hack is for Chrome and Safari, and the -ms-overflow-style hack is for Edge/IE.

So if you don’t like vendor-specific CSS, or if you think scrollbars are better for accessibility, then you can just keep the scrollbar around. Follow your heart!

Pinch-zoom

For pinch-zooming, this is one case where I allowed myself an indulgence: I use the <pinch-zoom> element from Google Chrome Labs.

I like it because it’s extremely small (5.2kB minified) and it uses Pointer Events under the hood, meaning it supports mobile touchscreens, touchpads, touchscreen laptops, and any device that supports pinch-zooming.

However, this element isn’t totally compatible with a scrollable list, because dragging your finger left and right causes the image to move left and right, rather than scroll left and right.

 

I thought this was actually a nice touch, though, since it allows you to choose which part of the image to zoom in on. So I decided to keep it.

To make this work inside a scrollable carousel, though, I decided to add a separate mode for zooming. You have to tap the magnifying glass to enable zooming, at which point dragging your finger moves the image itself rather than the carousel.

Toggling the pinch-zoom mode was as simple as removing or adding the <pinch-zoom> element to toggle it [2]. I also decided to add some explicit “zoom in” and “zoom out” buttons for the benefit of users who don’t have a device that supports pinch-zooming.

 

Of course, I could have implemented this myself using raw Pointer Events, but <pinch-zoom> offers a small footprint, a nice API, and good browser compatibility (e.g. on iOS Safari, where Pointer Events are not supported). So it felt like a worthy addition.

Intrinsic sizing

The last piece of the puzzle (I promise!) is a way to keep the images from doing a re-layout when they load. This can lead to janky-looking reflows, especially on slow connections.

 

Assuming we know the dimensions of the images in advance, we can fix this by using the intrinsicsize attribute. Unfortunately this isn’t supported in any browser yet, but it’s coming soon to Chrome! And it’s way easier than any other (hacky) solution you may think of.

Here it is in Chrome 72 with the “experimental web platform features” flag enabled:

 

Notice that the buttons don’t jump around while the image loads. Much nicer!

Accessibility check

Looking over the WAI Carousel Concepts document, there are a few good things to keep in mind when implementing this carousel:

  1. To make the carousel more keyboard-navigable, you may add keyboard shortcuts, for instance and to navigate left and right. (Note though that a scrollable horizontal list can already be focused and scrolled with the keyboard.)
  2. Use <ul> and <li> elements instead of <div>s, so that a screen reader announces it as a list.
  3. The smooth-scrolling can be distracting or nausea-inducing for some folks, so respect prefers-reduced-motion or provide an option to turn it off.
  4. As mentioned previously, use aria-label and aria-pressed for the indicator buttons.

Compatibility check

But what about IE support? I can hear your boss screaming at you already.

If you’re one of the unfortunate souls who still has to maintain IE11 support, rest assured: a scroll-snap carousel is just a normal horizontal-scrolling list on IE. It doesn’t work exactly the same, but hey, does it need to? IE11 users probably don’t expect the best anymore.

Conclusion

So that’s it! I decided not to publish this as a library, and I’m leaving the pinch-zooming and intrinsic sizing as an exercise for the reader. I think the core building blocks are simple enough that folks really ought to just take the native APIs and run with them.

Any decisions I could bake into a library would only limit the flexibility of the carousel, and leave its users high-and-dry when they need to tweak something, because I’ve taught them how to use my library instead of the native browser API.

At some point, it’s just better to go native.

Footnotes

1. For whatever reason, I couldn’t get the old scroll snap points spec to work in Edge. Sarah Drasner apparently ran into the same issue. On the bright side, though, a horizontally scrolling list without snap points is just a regular horizontally scrolling list!

2. The first version of this blog post recommended using pointer-events: none to toggle the zoom mode on or off. It turns out that this breaks right-clicking to download an image. So it seems better to just remove or add the <pinch-zoom> element to toggle it.

Things I’ve been wrong about, things I’ve been right about

The end of the year is a good time for reflection, and this year I’m going to do something a bit different. I’d like to list some of the calls I’ve made over the years, and how well those predictions have turned out.

So without further ado, here’s a list of things I’ve been wrong about or right about over the years.

Quick links:

Wrong: web workers will take over the world

Around 2015, I got really excited by web workers. I gave talks, I wrote a blog post, and I wrote an app that got a fair amount of attention. Unfortunately it turned out web workers were not going to take over the world in the way I imagined.

My enthusiasm for web workers mostly came from my experience with Android development. In Android development, if you don’t want your app to be slow, you move work to a background thread. After I became a web developer, I discovered it was suddenly very hard to make apps that weren’t janky. Oh, the web browser only has one thread? Well, there’s your problem.

What I didn’t know at the time, though, was that browsers already had a lot of tricks for moving work off the main thread; they’re just not necessarily very obvious, or directly exposed to web developers. You can see my ignorance in this video, where I’m purportedly showing the performance advantages of my worker-powered Pokémon app by scrolling the screen on mobile Chrome.

As I learned later, though, scrolling runs off-main-thread in modern browsers (and more importantly, composited). So the only thing that’s going to make this scrolling smoother is to not block it with unnecessary touchstart/touchmove listeners, or for the Chrome team to improve their scrolling implementation (as in fact, they have been doing). There are also differences between subscrollers and main-document scrollers, as I learned later.

All of these things are non-obvious to web developers, because they’re not directly exposed in an API. So in my ignorance, I pointed to the one case where threading is exposed in a web API, i.e. web workers.

While it is true, though, that blocking the main thread is a major cause of slowdowns in web pages, web workers aren’t the panacea I imagined. The reasons are laid out very succinctly by Evan You in this talk, but to summarize his points: moving work from the main thread to a background worker is very difficult, and the payoff is not so great.

The main reason it’s difficult is that you always have to come back to the main thread to do work on the DOM anyway. This is what libraries like worker-dom do. Also, some APIs can only be invoked synchronously on the main thread, such as getBoundingClientRect. Furthermore, as pointed out by Pete Hunt, web workers cannot handle preventDefault or stopPropagation (e.g. in a click handler), because those must be handled synchronously.

So on the one hand, you can’t just take existing web code and port it to a web worker; there are some things that have to be tweaked, and other things that are just impossible. Then on the other hand, moving things to a worker creates its own costs. The cost of cloning data between threads can be expensive (note: to be fair, Chrome has improved their cloning performance since I wrote that post). There is also a built-in latency when sending messages between the two threads, so you don’t necessarily want to pay that round-trip cost for every interaction. Also, some work has to be done on the main thread anyway, and it’s not guaranteed that those costs are the small ones.

Since then, I’ve come to believe that the best way to avoid the cost of main thread work (such as virtual DOM diffing in a library like React) is not by moving it to a background thread, but rather by skipping it entirely. SvelteJS does this, as explained in this talk by Rich Harris. Also, you can use APIs like requestIdleCallback to delay work on the main thread. Future APIs like hasPendingUserInput may help as well.

Of course, I’m not saying that web workers should never be used. For long-running computations that don’t rely on the DOM, they’re definitely useful. And perhaps sometime in the future it will become more viable to run your “entire app” in a worker thread, as sketched out here by Jason Miller. APIs like SharedArrayBuffer, blöcks, and some kind of asynchronous preventDefault/stopPropagation may tip the balance in web workers’ favor. But for now I’ll say that I was wrong on this call.

Wrong: Safari is the new IE

“Safari is the new IE” is probably my most-read post of all time, since it got picked up by Ars Technica. Unfortunately I’ve learned a lot about how the browser industry works since I wrote it, and nowadays I regard it with a bit of embarrassment.

As it turns out, Safari is not really the new IE. At the time I wrote that post (2015), the WebKit team was dragging their feet a bit, but since then they’ve really picked up the pace. If you look at metrics like HTML5Test, CanIUse, or ES2015+ compatibility tables, you’ll see they’ve made a lot of progress since 2015. They’re still behind Chrome and Firefox, but they’re giving Edge a run for its money (although Edge is now switching to Chromium, so that’s less relevant).

Also, the WebKit team does a lot of work that is less obvious to web developers, but which they still deserve credit for. Safari is a beast when it comes to performance, and they often set the standard that other engines aim to beat. It’s no surprise that the Speedometer benchmark came from the WebKit team (with Safari originally winning), and quickly became a point of focus for Chrome, Edge, and Firefox. The MotionMark and JetStream benchmarks also originally came from WebKit.

The WebKit team also does some interesting privacy work, including intelligent tracking protection and double-keying of cross-origin storage. (I.e. if example.com stores data in an iframe inside of another website, that data will not be shared with example.com itself or other example.com iframes on other websites. This limits this ability of sites to do third-party tracking.)

To be clear, though, I don’t regret writing that blog post. It was a cry of anger from a guy who was tired of dealing with a lot of IndexedDB bugs, which the WebKit team eventually got around to fixing. Heck, I’ve been told that my blog post may have even motivated Apple to make those fixes, and to release excellent developer-facing features like Safari Technology Preview. So kudos, WebKit team: you proved me wrong!

In any case, it’s unlikely that we’ll ever have a situation like IE6 again, with one browser taking up 95% of all usage. The best contender for that title is currently Chrome, and although it ticks some of the IE6 boxes (outsized influence on the ecosystem, de-facto standardization of implementation details), it doesn’t tick some other ones (lack of investment from the company building it, falling behind on web standards). The state of browser diversity is certainly worrying, though, which makes it all the more important to support non-Chromium browsers like Safari and Firefox, and give them credit for the good work they’re doing.

So in that regard, I am sorry for creating a meme that still seems to stick to this day.

Right: developer experience is trumping user experience

This blog post didn’t get a lot of attention when I published it in early 2016, but I think I tapped into something that was happening in the web community: web developers were starting to focus on obscure developer-facing features of frontend frameworks rather than tangible benefits for end-users.

Since then, this point has been articulated particularly well by folks on the Chrome team and at Google, such as Malte Ubl (“Developer experience and user experience,” 2017) and Alex Russell (“The developer experience bait-and-switch,” 2018). Paul Lewis also touched on it a bit in late 2015 in “The cost of frameworks”.

Developer experience (DX) vs user experience (UX) is now a topic of hot debate among web developers, and although I may not have put my finger on the problem very well, I did start writing about it in early 2016. So I’ll chalk this up as something I was right about.

Right: I’m better off without a Twitter account

I deleted my Twitter account a little over a year ago, and I have no regrets about that.

Twitter has made some good moves in the past year, such as as bringing back the chronological timeline, but overall it’s still a cesspool of negativity, preening, and distractions. Also, I don’t believe any one company should have a monopoly on microblogging, so I’m putting my money where my mouth is by denying them my attention and ad revenue.

These days I use Mastodon and RSS, and I’m much happier. Mastodon in particular has served as a kind of nicotine patch for my Twitter addiction, and for that I’m grateful.

The fediverse does have some of the same negative characteristics as Twitter (brigading, self-righteousness, lack of nuance), but overall it’s much smaller and quieter than Twitter, and more importantly less addictive, so I use social media less these days than I used to. I tend to spend more time on my hobbies instead, one of which is (ironically) building a Mastodon client!

Right: the cost of small modules

“The cost of small modules” was one of my most-read posts of 2016, and in terms of the overall conclusions, I was right. JavaScript compilation and initial execution are expensive, as has been covered quite well by Addy Osmani in “The cost of JavaScript”.

Furthermore, a lot of the bloat was coming from the bundlers themselves. In the post, I identified Browserify and Webpack as the main offenders, with Closure Compiler and Rollup showing how to do it right. Since I wrote that post, though, Webpack and Browserify have stepped up their game, and now module concatenation is a standard practice for JavaScript bundlers.

One thing I didn’t understand at the time was why JavaScript compilation was so expensive for the “function-wrapping” format of bundlers like Webpack and Browserify. I only realized it later when researching some quirks about how JavaScript engines parse function bodies. The conclusions from that research were interesting, but the larger takeaway of “only include the code you need” was the important one.

Mixed: progressive enhancement isn’t dead, but it smells funny

For better or worse, progressive enhancement doesn’t seem to be doing very well these days. In retrospect, this blog post was more about Twitter shaming (see above for my thoughts on Twitter), but I think the larger point about progressive enhancement losing its cachet is right.

As we slowly enter a world where there is one major browser engine (Chromium), which is frequently updated and leading on web standards, supporting old or weird browsers just becomes less important. Developers have already voted with their feet to target mostly Chrome and Chrome derivatives, putting pressure on other browsers to either adopt Chromium themselves or else bleed users (and therefore relevance). It’s a self-perpetuating cycle – the less developers care about progressive enhancement, the less it matters.

I also believe the term “progressive enhancement” has been somewhat co-opted by the Chrome devrel team as as euphemism for giving the best experience to Chrome and a poorer experience to “older browsers” (aka non-Chrome browsers). It’s a brilliant re-branding that feeds into web developers’ deepest wish, which is to live in a Chrome-only world where they only have to focus on Chrome.

That’s not to say progressive enhancement is without its virtues. Insofar as it encourages people to actually think about accessibility, performance, and web standards, it’s a good thing. But these days it’s becoming less about “build with HTML, layer on CSS, sprinkle on JavaScript” and more about “support a slightly older version of Chrome, target the latest version of Chrome.”

The other point I made in that blog post, which was about JavaScript-heavy webapps being better for the “next billion” Internet users, may turn out to be wrong. I’m not sure. Static websites are certainly easier on the user’s battery, and with a Service Worker they can still have the benefits of offline capabilities.

Perhaps with the new Portals proposal, we won’t even need to build SPAs to have fancy transitions between pages. I have a hunch that SPAs are being overused these days, and that user experience is suffering as a consequence, but that’s another bet that will have to be evaluated at a later date.

Conclusions

So that’s all for my roundup of bad takes, good takes, and the stuff in between. Hope you found it interesting, and happy 2019!