Come To Daddy – West End

A bar with a neon sign out the front reads "Daddy", and a bunch of people stand around out the front. Their faces are blurred because, y'know, they didn't ask to have their photo taken.

I wrote a few weeks back about the opening of a local queer bar, Come To Daddy. I really like it, it’s nice to have it just down the road, and they do food and a decent mocktail.

But it’s been my bugbear that they don’t have a website and I have to go to Instagram to find out what’s happening. I had to sign up for an account because it seems half the internet is behind Meta’s paywall these days.

Anyway I just checked my bank statement and guess who has a website after all?

That’s right, cometodaddy.au (West End gay bar) has existed all this time, just it hasn’t shown up in search results. So I thought I’d link to it and maybe prompt the spiders to start spidering.


We went for lunch this Sunday. We rocked up at 1:30, which is when their brunch menu ends and their regular menu begins. We saw a couple of massive big breakfasts go past, but I was more interested in the parmi (parma on the menu).

The food was alright. The Croque Daddy wasn’t super crunchy, and the schnitzel was on the cheaper side, but the chips were great and the coleslaw was the star of the show. It really surprised me because I don’t usually even like it, but this was super fresh and the dressing was really nice.

I rate the place. This wasn’t fine dining, but it was about what I expected. I’ll have to head back for the brunch sometime. Want to come with? Hit me up.

Friday at 01:28 – a letter to Airservices Australia

An Ansett Australia boarding pass, with the most recent logo. It's pretty ratty

It starts as an indeterminate sound, I don’t even know how to describe it. But shortly after you get the high frequencies and you know it’s a plane. As it gets closer you hear the air start to tear apart, the echo all around the concrete canyons of the apartment complexes. Until finally, the bass rumble in your chest.

As it passes overhead, or even as it passes a suburb across, I can look out my window and see the planes. My bedroom window.

Then, as soon as it’s arrived, it’s gone again. Fading out into the distance and you’re left wondering, when will the next one come? Is that it, can I hear it? Or is it my imagination?

Sometimes, the next one will arrive immediately. You’ll have plane after plane after plane, a constant roar like the other morning starting at 6am. Sometimes there’s only a couple, like tonight.

A couple at midnight.

Again at 1am.

The 01:55 flight to Dubai.

No rhyme or reason.

I’m laying here trying to get to sleep, but my chest is just filled with anxiety. It’s a terrible sound. It’s a terrible thing to live with these machines flying across your home at all hours.

It’s terrible that someone made the decision to deliberately change the flight path.

There are tens of thousands of people living in this suburb, let alone all the others.

And I wonder how much the noise pollution is taking its toll on them. As it is me.

Using the cache API to store huge amounts of data in the browser

Recently I came across the Cache API. It’s used in Service Workers to prefetch/cache files for your offline web app, but it’s also available outside of workers.

I was looking at using the Cache API to cache generated images, to speed up a WebGL piece, without crashing Safari. Never got around to it, but now I’m looking at it, it’s kinda easy:


To open a cache and save an arbitrary string:

caches.open('SomeCacheName').then(async cache => {
    // put something in the cache
    await cache.put('SomeKeyName', new Response('Hello world'));

    // Get something back out of the cache
    const response = await cache.match('SomeKeyName');
    const text = await response.text();
    console.log(text); // Hello World
});

You can then reopen that cache at any time in the future to fetch your value:

caches.open('SomeCacheName').then(async cache => {
    const response = await cache.match('SomeKeyName');

    // treat the response object as you would a fetch() response
    const text = await response.text();
    console.log(text); // Hello World
});

While this example uses strings (retrieved with response.text()), you can store any number of formats including Blobs and ArrayBuffers. See the Response constructor for ideas.


The benefit of using the standard old browser cache is that you can store a LOT of data. My Mac reports the following:

navigator.storage.estimate().then((estimate) => {
    console.log('percent', (
        (estimate.usage / estimate.quota) *
        100
    ).toFixed(2));
    // percent 0.00

    console.log('quota', (estimate.quota / 1024 / 1024).toFixed(2) + "MB");
    // quota 10240.00MB
});

That’s 10 gigabytes of storage available. To be fair, not everyone will have that much space, but you get the idea.

It also needs to be cleaned up manually, otherwise it will sit in the cache permanently taking up space (unless the cache is cleared). The MDN page says:

The browser does its best to manage disk space, but it may delete the Cache storage for an origin. The browser will generally delete all of the data for an origin or none of the data for an origin.


I haven’t used this in production yet, but it seems to work fine. And browser support looks good. So let me know if this is useful.

For more reading, check out the MDN Cache API.

Solo camping with my EV

I took this week off to unwind. What better way than out in the bush, adjacent a creek, sitting next to a campfire watching satellites spin past?

I’ve wanted to get back into camping for a while now. I figure it’s a good way to get some downtime without spending a fortune on accommodation. The national parks in Queensland are seven bucks a night, which is a cheap weekend away these days, and there’s plenty of them.

The Qld Government has a website listing all their campgrounds, and you can filter by things like toilet availability, whether or not you need a four wheel drive to access.

I was originally planning to drive north but after checking the forecast figured south had less likelihood of wet and I landed on a little campground called Spicers Gap Campground.


It feels like a lot of things went wrong, but I figure the best way to approach them is with a sort of zen mindset. The first omen, I ordered a V2L cable for my EV so I could power my cooktop and assorted bits. It was sent express post last week but Australia Post managed to lose it somewhere in Melbourne. So I picked up a small gas stove instead.

my package did not arrive on time

Then I forgot to bring my water bottle AND the 15l tank of water, both sitting on my kitchen bench. So I had to stop in and get some bottled water.

One cool thing was finding an EV charger on the way at the Queensland Raceway, and it’s completely freeeee. It’s advertised as some ridiculously high speed charger, but I only got about 80 kilowatts, enough to charge the EV up while I ate a sandwich (ham cheese & tomato, stale, from the same fuel station I got the water).

Once I’d topped up to 80% I drove the hour or so south to the campground, only to hit a dirt road up a steep hill with a sign saying “4×4 ONLY”.

Rocked up to the camping area that the website said is most definitely available to 2 wheel drive vehicles.

I checked the website and yeah, it says it’s only accessible by four wheel drive. But it’s also listed as a regular 2WD accessible camp. So I checked to see if there were any other access roads (there weren’t), then had a look to see if there were any other campgrounds nearby (kinda), before practising my calm and driving off.


Manna Gum, the nearby camp was only 13 km away, but the drive up through the Great Diving Range and looping back on a long and windy road was 85 km.

Manna Gum and Spicers Gap campgrounds are pretty close together, but 85 km by road

I arrived about an hour later and un-thworped my pop-up tent in a little spot the furthest away from the only other people at the camp.

Some observations:

  1. There’s no mobile signal (Spicer’s Gap would have been on the cusp of service, so I bought a Telstra SIM specially for the occasion)
  2. There are cows! Lots of them, just chilling around the place.
  3. It’s really nice, it’s a eucalypt rainforesty looking place, and I can hear the creek flowing nearby. Can’t wait to check it out.
  4. It was also much further away than I thought. I went from 80% to 43% battery after the little detour. I know that the cooktop doesn’t use THAT much power, but I’d still feel a bit of that range anxiety getting back.
  5. Especially because there’s no signal I can’t look up nearby chargers hahahahaaaaa oh no.

It was at this time I realised I forgot to bring the ropes for my tarp, and the eggs for breakfast. Almost like I should have a checklist for this stuff.

a car and a tent and a little table covered in things in a campground, with lots of trees and a little dirt path

It was a nice afternoon though. I didn’t really do anything other than set up the campsite and sit around watching cows while trying to get the fire going. It took a little bit of coaxing, because I didn’t really have any kindling. But I got one of the thinner logs to catch fire eventually and it kept itself mostly going all night.

When the sun started to go down, a big swarm of cockatoos tore open the sky with their screeching. A couple of wallabies came out of the trees to snack on the grass, and a big fat possum jumped on the ground right behind me which gave me a fright. Can’t get away from them it seems.

So it was only myself and the campers up the hill at the campgrounds. At the other campsite it was only going to be ME all by myself. Shawn asked if I’d get scaredy out in the wilderness on my own, but there’s not really any wildlife to cause troubles here. Maybe overinquisitive cows?

Though when I went to book the original booking the site told me there were a certain number of campsites at the camp, and also that the same number of campsites were available to book, and I realised that anyone could just scrape that data and find campsites with 1 person staying by themselves. And go steal their nickels or whatever tech savvy criminals might do. So probably not great opsec there, so I booked for two people instead of one because I figured it’s not that much more expensive and it might give said crimbinals pause. I’m thinking about mailing the department behind it to suggest they don’t do that any more.

But overall it was fine, and I’m happy to say that my first night was tranquil AF watching the animals, the stars, the fire. Brain off, relaxation.

Dinner was sausage gnocci.

The moon behind some clouds behind some trees. It's not spooky unless you want it to be.

Around 8 o’clock the fire sort of gave up firing and I decided to go to bed to write some blog.

It was that point I realised the tent I bought is too small. It looked alright by the specs. It’s longer than I am, therefore all good? Turns out nah, I can’t lay down and type without both the laptop and my feet brushing the canvas. I think it’ll be alright to sleep in. A bit squishy, but alright. But I think I’m gonna have to upgrade at some point in the future.

So I’m laying here, finishing up, listening to the creek running down the hill and I’m pretty happy.

Also I forgot to bring a pillow.


The next day I woke up to the sound of a strange bird calling and warbling outside my tent. There were a few of them, and I heard them crashing around outside. Or maybe that was the wallabies.

I didn’t look because it was 6 o’clock and I desperately wanted to sleep. Also it was raining and I desperately didn’t want to get wet.

My choice in tent didn’t help there. It’s the Pavillo Cool Mount 2, another cheapy after my last one leaked. This one also leaked, with droplets beading in where the drizzle was pooling, and also where the front zip just wasn’t sealed at all. Also because it’s smaller, I couldn’t get dressed properly without rubbing all over the wet canvas and getting even wetter. I don’t rate this tent highly, I’m just waiting for it to dry out before I take it back and complain. If you have a favourite small tent for big people that can withstand a light drizzle, let me know!

But ultimately the rain was more than I expected and I wasn’t going to do any hiking or exploring in that weather so I packed up and headed back, considering it a very wet dry run for a proper camping trip another time.

The EV made it back to the free charger with 20% battery to spare. I think it ate up a lot of power defrosting the windows, but it also regenerated 3 kilowatts driving down the Great Dividing Range, so I was pretty stoked.

All up, I had a great trip even though it was a bit chaotic. I’m looking forward to doing the next one properly. Let me know your favourite camping spots!

Brisbane comedy festival, queer spaces, and EV races

This weekend has been a blast. It’s the Brisbane Comedy Festival so on Friday I went to see Josh Thomas’ Lets Tidy Up.

Comedian, gay, neurodiverse, and ex Brisbane kid. I loved his TV stuff and I just find him endearing, so I booked two tickets and couldn’t find anyone other than Ben who would go with me.

This is a show where Josh tidies up. Tidying up is not normally the stuff of gripping drama, it wouldn’t be a propulsive narrative for most people, but for Josh it’s Everest, a fundamentally impossible task, like trying to defy the moon and control the tides.

Brisbane Comedy Festival Program

We had Korean for dinner, then decided to drive because the Powerhouse is not well connected to public transit (the CityCat takes AGES) and it was threatening to rain.

We couldn’t get parking so we ended up driving around and taking scooters the rest of the way anyway. But it was a fun night.

The Powerhouse from riiiiight up the back, the stage is lit green and has the words Josh repeated over and over. People are still taking their seats.

The next day I was going to have breakfast with another friend and I’m actually kind of glad it was cancelled because I was not prepared to wake up that morning. Thankfully neither was she, so I had a bit of a sleep in and sat around doing not much until the afternoon.

Then I caught up with Dan who was in the area for the grand opening of a new local queer bar Come To Daddy. Hilarious name, puns abound.

It’s a 5 minute walk from my place but I hadn’t heard anything about it! It’s not really in Google and it seems like they’re only on Instagram. And even so, only barely ¯_(ツ)_/¯

But we wandered down shortly after opening, grabbed a drink and a seat, and people filtered in until it was standing room only. Then there was an amusing welcome to country to christen the bar (yarma!), and I saw my first ever drag queen pianist (playing “Daddies Everywhere” to the tune of “Love is in the Air”).

The queue for the bar was super long so we only had a couple of drinks before calling it a night. But I’m keen to head back soon.

Dan and I, two guys taking a selfie on the street in front of a crowded but not very in-focus bar.

The next morning I went to the little “health food store” and bought some “health food” (chocolate coated freeze dried strawberries and ginger), then headed down to Amanda and Colin’s place in my new EV.

I got the MG4 EV because I’ve been doing a lot of travel back and forth to my parents place in Maryborough, which emits about 70 kilos of CO2 round trip. Apparently when you burn fuel in an engine it doesn’t just disappear, who knew! Also it’s a lot more comfortable than the Hyundai Getz I was borrowing from my folks, with all the mod cons like cruise control and headlamps that actually illuminate the road.

I arrived a bit early so when I took a wrong turn I was happy to just meander around the streets admiring how much the suburbs of the Gold Coast look kinda just like the suburbs where I grew up north of Brisbane. I also went up a hill and cruised right back down, excitedly watching the regenerative braking put a bunch of power back into the batteries.

I said I would bring “some sausage rolls or something”, but Amanda really wanted to put on a Scottish lunch. So we had scotch pies with mash and beans, and Irn Bru as a palate cleanser. It was amazing and I felt very special.

I don’t have any pics, other than this one where I found the cap for the charger had been flapping around where I forgot to replace it for the past week.

the cap for the charger had been flapping around where I forgot to replace it for the past week

By the time I got home it was already kinda late so I made dinner (I got the tastiest turkish bread from Coles yesterday after visiting Daddy), then started faffing around with my web site.

I wanted a place to share links to stories I’ve enjoyed reading, or would like to refer back to. So a few weeks back I set up a bookmarks page, as well as an RSS feed that you can subscribe to wherever you get your feeds.

But as I was testing the RSS feed I had to go digging around in my subscriptions, and it was kind of nice to go back and see all the feeds from various people I’ve subscribed to over the years. If I stumble on an interesting looking personal site I try to subscribe, and keep that small web spirit alive.

Some people post once every few years. Some people never post again. And long story short that’s why I took a moment to write about my weekend. Also it’s #WeblogPoMo2024 (HT rachsmith.com).

So how about you, what have you been up to?

I have a fish tank now

I set up a fish tank.

A fish tank with duck weed and airline tube rings so you can see the plants below

It’s a low tech planted tank, using the Walstad method. Kinda like the one described here.

The TLDR is that it’s chock full of plants and critters that maintain the water quality without needing to do lots of cleaning, water changes, or filtering. And it’s cute!


Sometime last year I was admiring the betta fish at Mappins and Ben encouraged me to get one. But I wanted to read up on them first, and it turns out bettas prefer larger tanks, lots of space, things to do. Don’t we all?

So instead of getting a betta I decided to set up a planted aquarium because it seemed like a cool hobby.

Given it’s full of soil and some people haven’t had luck setting up this style of tank, I was cautious about the potential for the water quality go weird and gross. But it was perfect from the moment I set it up. There was a tiny nitrite spike at the start, but it settled down and I haven’t even registered any nitrates. It’s been a pretty steady PH around 7.6 which is about what the tap water is. So all the numbers have been super stable, presumably because of the plant load!

I did a 50% water change maybe a month in. Not because I needed to, but because I wanted to give it a try. It did improve the colour of the water, but it’s been fine since.


One of the coolest things were the critters that hitched a ride along with the plants.

I had a population of bladder snails (they can float!!!) and seed shrimp (so cute!!!) explode in the tank. This was a great way to start off the ecosystem, because the snails and shrimp were a great cleanup crew. I can’t stress enough how much detritus used to be on the bottom of the tank, but now it’s perfectly clean.

After I was sure the water quality was stable, I picked up some shrimp. They’re yellow cherry shrimp (neocaridinas), and I also accidentally picked up a transparent one which I didn’t realise until it was in the bag.


The shrimp are great. They scrounge around the looking for tasty treats, and clean up lots of dead stuff. They swim and crawl around the tank, and sometimes make me think they’re dead when they stop moving in weird positions. They’ve got a lot of personality.

Maybe a month after the shrimp were settled in I went back to Mappins because I saw they had ember tetras – a teeny tiny fish that I was hoping wouldn’t eat my seed shrimp.

I was wrong, they ate the seed shrimp. But they’re the perfect size for such a small tank at up to 2cm long. And once they’re comfortable they’re quite an outgoing and social fish.

They also never sit still so they’re impossible to get a photo with my phone camera.


Anyway, it’s been three months and the tank has gotten overgrown. So I gave the tank a bit of a trim. I didn’t do all of the plants but took a lot of them down and replanted them to make a thicker forest.

I think I freaked the fish out, because they’re all schooling again, rather than out exploring by themselves.

I also found a bunch of the shrimp that were in hiding. I’ve definitely got at least five in there. A bunch of them were chilling under the pump, I guess they like cleaning out the gunk in there and it’s a good hiding spot 😌


I also threw together a quick Walstad style jar. Partly because I thought it was cute, and partly because I know Ben wants to start a tank and it’s a good way to keep some cuttings alive. I’m very curious if the jar will work out or not, but from everything I’ve read it should be fine.

So that’s my three month tank.

A jar with soil, sand, and aquatic plants

Why I no longer recommend GoPro (cw swears)

Cos they sucked me into a free trial and charged my card a year later without my realising. I didn’t even know I was subscribed.

Further, as an Australian there’s no way to contact them because they work on US business hours. So I was up at 2AM the other night hanging off a chat box where Vincent was apologetic, but kept pasting prefabricated blocks of text telling me to get fucked.

It’s enshittification at its best. They’re beholden to investors, and have to invent junk services to get that sweet recurring revenue. And their policy is no refunds, ever. So it’s obviously deliberate.

So basically, fuck em.

Insta360 and DJI both have excellent products in this space that may have their own ideological foibles, but at least won’t brazenly steal money from your card.

My script to auto-delete Google Maps reviews

Google Maps like much of the web has devolved into an AI generated slurry.

Nowadays every review I leave gets a reply “from” the business which is clearly generated by a machine. All the photos are inevitably going in to train Gemini. And the level-up gamification was fun at first but led to nothing more than endless grinding to reach the next level.

Anyway, I’m out. Have been for a little while. But I’ve left a trail of photos and digital detritus I’d rather clean up.

Google doesn’t have a way to bulk-delete your stuff (for obvious if annoying reasons) so I thought I’d write a little script to do it for me.

We're in Google Maps and there's an automation running to delete photos from the Local Guide section.

The scripts

There’s two scripts, one for photos and one for reviews. They’re pretty naive and need to be restarted from time to time but they should be safe enough.

Huge disclaimer: these will probably get out of date at some point and may not work. You should read and understand the code before you do anything with it.

For photos, make sure you’re on the “Photos” tab of the My Contribution section (see above), then I ran the following in the console:

(async () => {
  const sleep = (time) => new Promise(resolve => setTimeout(resolve,time));
  const go = async () => {
    // click the kebab
    document.querySelector('button[jsaction*="pane.photo.actionMenu"]:not([aria-hidden="true"])').click();
    await sleep(200);
    
    // click the delete menu item
    document.querySelector('#action-menu div[role="menuitemradio"]').click();
    await sleep(200);
    
    // click the "yes I'm sure" button
    document.querySelector('div[aria-label="Delete this photo?"] button+button,div[aria-label="Delete this video?"] button+button').click();
    await sleep(1500);
    
    // check if there's any left, and do it all again
    if(document.querySelector('button[jsaction*="pane.photo.actionMenu"]:not([aria-hidden="true"])')) go();
  }
  go();
})()

And for reviews on the reviews tab:

// delete reviews from Google Maps
(async () => {
  const sleep = (time) => new Promise(resolve => setTimeout(resolve,time));
  const go = async () => {
    // click the kebab
    document.querySelector('button[jsaction*="review.actionMenu"]:not([aria-hidden="true"])').click();
    await sleep(300);
    
    // find the delete review menu item
    const deleteMenuItem = document.querySelector('#action-menu div[role="menuitemradio"]+div');
    if(deleteMenuItem.textContent.trim() !== 'Delete review') return console.error('wrong menu item', deleteMenuItem.textContent);
    deleteMenuItem.click();
    await sleep(300);
    
    // click the "yes I'm sure" button
    document.querySelector('div[aria-label="Delete this review?"] button+button').click();
    await sleep(2000);
    
    // check if there's any left, and do it all again
    if(document.querySelector('button[jsaction*="review.actionMenu"]:not([aria-hidden="true"])')) go();
  }
  go();
})();

These are also on Github because my blog code formatting isn’t great. I should fix that up sometime.


What I learned hacking Google Maps (lol)

This code is pretty naive, and breaks a lot. I had to go back in a few times to restart the script, or adjust the timings when something broke. But it got there in the end.

I do appreciate the simplicity of the sleep() function/promiseified setTimeout. This isn’t in the language because it’s generally better to attach some kind of event or observer or do some polling to make sure the app is in the correct state. But in this case I thought it was a fairly elegant way to hack together a script in 5 minutes.

I could make this faster and more stable by implementing some kind of waitUntil(selector) function to await the presence of the menu/dialog in the DOM. But I would also need a waitUntilNot(selector) to wait for the deletion to finish. In any case that’s more complex, and we don’t need to overengineer this.

Anyway, the second script is done now and all my photos and reviews are gone. I’m still somehow a level 6 local guide (down from a level 7) so good for me.

Screenshots from Google Maps: you haven't written any reviews yet, add your photos to Google Maps + Ash is a Local Guide level 6

Human made web button

I’ve been thinking a lot about the problem of garbage AI generated content on the web. It’s a problem that erodes trust and makes everyone look bad. So how do you combat that?

Web buttons, of course!

A bunch of old school 88x31 web buttons (advertising netscape, IE, Acrobat reader, etc), with my new one with rainbow text "Human Made" and a cute pixel art android with a red strikethrough
I took a minute to make a colourful web button, I hope you like it.

These were popular in the early 2000s as a way of adding pieces of flair to your website, to link to friends, and to show off your W3 validated XHTML and CSS.

A web badge that says Valid XHTML 1.0 A web badge that says Valid CSS

There’s a few archives around showing these off, including The 88×31 GIF Collection and this collection of old buttons. (edit: also Pixel Sea by Daniel aka Melon is a delightful way to explore buttons)

And given my web site is of a certain vintage I thought I’d do something up in the style.

I think it worked out really well. The little robot is very cute, and the colours were kind of inspired by the old Amiga button. And as was common at the time I exported it and closed before remembering to save my sources, so this is it now.


If you want to download and use the button on your own website, go for it. It’s all yours. Use it as you wish:

A cute android crossed out, and rainbow text reads 'human made'.   A cute android crossed out, and rainbow text reads 'human made'.

I’ve decided to upscale it here for effect, but the original version is indeed 88×31 pixels.

If you want to upscale it, you can add the css image-rendering:pixelated; to make it render pixel perfect, rather than using a more blurry modern scaling designed for photos.

Dev log: Debugging Safari, an ogre with layers

We’re releasing a WebGL feature soon, and let me tell you Safari has lived up to its reputation as the new Internet Explorer.

We’ve had two big issues:

  1. Safari completely crashes the tab with “a problem repeatedly occurred”
  2. Safari WebGL rendering flickers when scrolling the page

Safari completely crashes the tab with “a problem repeatedly occurred”

The most concerning issue was the Safari crash, which only happened on iOS, not the simulator. I don’t have any iOS devices to test with, so I made the decision to get myself an iPad mini. It’s a write off!

Anyway I’m now the proud owner of a cute lil purple iPad and it still crashes so that’s a good thing, now I can work out how to fix it.

After debugging for way too long, I worked out this was a memory usage issue. Even though Safari runs on some of the most powerful mobile hardware around, it has a hard limit on how much RAM a web page can consume. Even when that page is open. So you can’t make full use of the device in Safari.

My problem had several causes:

  1. I was loading multiple WebGL interactives on page load. Deferring these until the user scrolls them into view helped fix the issue.
  2. I was caching WebGL textures to make the interactive feel snappier. In the end I had to remove all the caching optimisations to get under the Safari memory limit, and now each texture is rendered in realtime when it’s needed.
  3. On top of this, I was hitting an issue with too many layers in Safari causing excessive memory usage.

Compositing layers are created when animation happens in your page. Much like old cel animated films, the browser keeps content that isn’t likely to change in separate layers so it can sandwich animated layers in between, without having to draw everything all over again. For instance a parallax effect will have a background layer and a foreground layer, moving at different speeds.

A pink panther cel animation, showing the panther being lifted off the background.
An example of animation layers drawn on old school cel sheets. The pink panther can be added and removed from the scene without redrawing the layers underneath. Via The Art Professor.

Layers in Safari are created by a number of things, including:

  1. 3d transforms – e.g. -webkit-transform: translateZ(0);
  2. the will-change property – (intended to be used as a last resort. It should not be used to anticipate performance problems)
  3. Canvas layers – canvas is designed to be drawn and redrawn at arbitrary times, so the browser keeps it on its own layer.
  4. position:sticky/position:fixed – similar to parallax effects, these are just a part of doing business and we can’t optimise them any further

In our case, we had a number of 3d transforms and unnecessary will-change properties creating extra layers. These were contributing to Safari crashing. Cutting down on these layers stopped our page from crashing.


Safari WebGL rendering flickers when scrolling the page

This one was killing me because I couldn’t reproduce it on the iPad, and I don’t have an iPhone to test on.

This mostly seemed to happen in in-app browsers (like Slack), but this morning a colleague was able to reliably reproduce it in Safari proper, and I was able to reproduce it in the simulator.

It only seemed to happen while scrolling text boxes over the WebGL canvas So my assumption was that something was clearing the canvas without drawing the scene back in. The app is creating a separate offscreen WebGL instance for performing calculations and I assumed it might be some sort of weird race condition.

I tried a number of fixes that didn’t help:

  1. Render every frame, regardless of whether render is needed (i.e. don’t pause rendering when the scene hasn’t changed. Based on this similar bug).
  2. I tried enabling preserveDrawingBuffer in the renderer, because it seemed related. No dice.
  3. Disable anti-aliasing (per this bug, where Mapbox GL and Three.js fight it out in the same WebGL context)
  4. Downgrade to WebGL 1 (instead of 2). I can’t find the original post suggesting this, but it didn’t do anything.

The actual bug in my case was completely unrelated to Three.js.

When the screen resizes, I update the canvas size, the camera aspect, and projection matrix so that the canvas scales to fit its new dimensions:

  resizeToRoot() {
    const rect = this.root.getBoundingClientRect();
    this.renderer.setSize(rect.width, rect.height);
    this.camera.aspect = rect.width / rect.height;
    this.camera.updateProjectionMatrix();
  }

The problem is that this code doesn’t rerender the scene. So after it’s run the frame is left blank until the next requestAnimationFrame runs.

This wasn’t a huge problem, except when the Safari chrome disappears off the page the browser triggers a whole bunch of resizes in rapid succession. These were resizing the scene, and resulted in black frames until the scene rerendered, multiple times per second. And it didn’t happen on the iPad because the chrome never disappears offscreen.

Adding a this.renderer.render() to the resize function was a somewhat inefficient but effective fix.