The other day, I was working on a rather complex bug on wunderground.com. This site overlays a weather radar animation on top of a Google Maps-based map.
The problem was that when the animation has been left open for a while, one of the “frames” of the animation will be stuck in the first state from whenever you opened the map.
The site renders radar data with layers of 256x256 px big CANVAS elements, about 6x6 elements in each layer. Layers are hidden with opacity:0 and one at a time is shown from an interval.
Since my first attempt at understanding the architecture was looking at the DOM in the inspector I spent quite some time digging around among the hundreds of CANVAS elements trying to figure out how they were used and which ones were part of the layer with outdated data.
Eventually I realised that the site loads radar graphic PNG URLs with embedded timestamp and coordinate data, like this one (where the ‘66’ and ‘94’ have something to do with coordinates of the square in a grid):
These images are painted into the corresponding CANVAS elements.
Each 6 minutes, the site decides to load a new set of graphics. While doing so, it will destroy all existing CANVAS tiles to re-add and repaint them. This quirk made for a rather annoying debugging experience - every once in a while when I was digging around in the DOM the whole thing would get reset. If I had added properties or IDs to CANVAS elements and their parents to be able to locate them more easily, those would go away too.
Further down in the same method, it’s evident that enabling debugging will add some more information to the DOM. What I’m most interested in here is what URLs are used to paint each CANVAS element. There’s an l variable which refers to the URL - what if I set another conditional breakpoint just after the line where the site added its own debug data to the page? void(b.innerHTML += ‘ ‘ + l) might do:
This let me see the URLs that were used for each CANVAS element right there in the page itself:
And this led to the breakthrough: the URLs shown for the CANVAS elements that “jump back in time” have a different format than the others. These URLs do not have embedded timestamp data - they look like this: radartiles-ak4.wxug.com/tiles/8/66/94.png
The site seems to leave such caching decisions up to the browser - it sends Last-Modified and ETag, but no Cache-Control. I am no expert on the caching side of HTTP though. Perhaps we should revalidate each time the URL is required? Perhaps not? At least now we know why it happens - and can draw a few lessons for debugging too:
- It can be surprisingly useful to trigger inert debug code in websites. Several years ago, I had a hack replacing all instances of //\salert/( with just “alert(“ on all sites, and it could be quite entertaining
- Avoid trying to inspect 500 CANVAS elements unless you really have to..
- Using conditional breakpoints to “inject” code and change script behaviour can be very useful
If you didn’t know about this trick yet, I hope you’ll start using it and find it useful.