Playing hide and seek in Kennilworth Castle. You’ll never find me!
(geotagged for your convenience)
Never underestimate the power of people who are motivated by the good they can do in the world.
Today I was in awe of this team of unpaid volunteers who, having already given up their bank holiday weekend, worked through dinner and into the night to ensure the continued uptime of a piece software that enables the listening service of emotional support and suicide helplines.
This checkin to GCA2025 Where's 25? - Locationless Cache reflects a geocaching.com log entry. See more of Dan's cache logs.
Happy 25th birthday, geocaching.
I’m spending the weekend volunteering for a nonprofit I founded (it’s almost as old as geocaching, at 23). We’re staying in a hotel at N 52° 36.184′ W 001° 53.869′. I’ve also gotten out to find a couple of local geocaches.
But guess which room number the hotel have given me…
This checkin to GC7H5M9 Hedge your bets reflects a geocaching.com log entry. See more of Dan's cache logs.
Found following a short hunt and a brief rummage after walking up the nearby footpath, with the help of a previous log which talked about crossing a fence (which I didn’t need to do, having come up the correct way in the first place).
Some fellow volunteers and I are meeting at a hotel to the West of here for a weekend of making software to help charities. When we meet up, I have a tradition of getting up early and finding a geocache or two before breakfast. Having exhausted the very-local supply of caches on previous visits, and not wishing to miss out on the tradition on this, geocaching’s 25th birthday, I decided it was time to come further afield (and to finally solve this puzzle!… I’m still stumped by its sibling, though!).
Nice container. Log slightly damp, but still usable. TFTC, and FP awarded for the enjoyable (once I spotted a pattern!) puzzle.
This checkin to GC9QD6R Centurion reflects a geocaching.com log entry. See more of Dan's cache logs.
Woke early, as usual, for the first day of a weekend of volunteering, while staying at nearby Fairlawns. I’ve already tapped out the most-local caches to that hotel on previous stays, so for this morning’s walk I came further afield to find this (and one of the two puzzle caches not too far away; that’s next, hopefully!)
This nice (topical!) container was an easy find once I poked my head into the right place. TFTC!
Happy 25th birthday, geocaching!
I’m travelling by train in just over a week, and I signed up an account with Avanti West Coast, who proudly show off their Shaw Trust accessibility certificate.
Clearly that certificate only applies to their website, though, and not to e.g. their emails. When you sign up an account with them, you need to verify your email address. They send you a (HTML-only) email with a link to click. Here’s what that link looks like to a sighted person:
So far, so good.
But here’s the HTML code they’re using to create that button. Maybe you’ll spot the problem:
<a href="https://www.avantiwestcoast.co.uk/train-tickets/verify-email?prospectId=12345678&customerKey=12345:1234567" target="_blank" style="font-family:Averta, Arial, Helvetica, sans-serif;"> <div class="mobile_image" style="font-family:Averta, Arial, Helvetica, sans-serif; display:none; border:none; text-align:center;"> <img width="40%" src="https://image.e.avantiwestcoast.co.uk/lib/fe371170756404747d1670/m/1/9069c7a2-b9ed-4188-9809-bf58592ec002.png" alt="" style="font-family:Averta, Arial, Helvetica, sans-serif;width:143px; height:40px;" /> </div> </a>
Despite specifying the font to use three times, they don’t actually have any alt text. So for somebody who can’t see that image, the link is completely unusable1.
This made me angry enough that I gave up on my transaction and bought my train tickets from LNER instead.
Accessibility matters. And that includes emails. Do better, Avanti.
1 Incidentally, this also makes the email unusable for privacy-conscious people who, like me, don’t routinely load remote images in emails. But that’s a secondary concern, really.
The video below is presented in portrait orientation, because your screen is taller than it is wide.
The video below is presented in landscape orientation, because your screen is wider than it is tall.
The video below is presented in square orientation (the Secret Bonus Square Video!), because your screen has approximately the same width as as its height. Cool!
This is possible (with a single <video>
element, and without any Javascript!) thanks to some cool HTML features you might not be aware of, which I’ll briefly explain
in the video. Or scroll down for the full details.
I saw a 2023 blog post by Scott Jehl about how he helped Firefox 120 (re)gain support
for the <source media="...">
attribute. Chrome added support later that year,
and Safari already had it. This means that it’s pretty safe to do something like this:
<video controls> <source src="squareish.mp4" media="(min-aspect-ratio: 0.95) and (max-aspect-ratio: 1.05)" /> <source src="portrait.mp4" media="(orientation: portrait)" /> <source src="landscape.mp4" /> </video>
squareish.mp4
which is shown to people on “squareish” viewports, failing that portrait.mp4
which is shown to
people whose viewports are taller than wide, and failing that landscape.mp4
which is shown to anybody else.
That’s broadly-speaking how the video above is rendered. No JavaScript needed.
Browsers only handle media queries on videos when they initially load, so you can’t just tip your phone over or resize the window: you’ll need to reload the page, too. But it works! Give it a go: take a look at the video in both portrait and landscape modes and let me know what you think1.
Here’s another cool technology that you might not have realised you could “just use”: adaptive bitrate streaming with HLS!
You’ve used adaptive bitrate streaming before, though you might not have noticed it. It’s what YouTube, Netflix, etc. are doing when your network connection degrades and you quickly get dropped-down, mid-video, to a lower-resolution version2.
Turns out you can do it on your own static hosting, no problem at all. I used this guide (which has a great description of the parameters used) to help me:
ffmpeg -i landscape.mp4 \ -filter_complex "[0:v]split=3[v1][v2][v3]; [v1]copy[v1out]; [v2]scale=w=1280:h=720[v2out]; [v3]scale=w=640:h=360[v3out]" \ -map "[v1out]" -c:v:0 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:0 5M -maxrate:v:0 5M -minrate:v:0 5M -bufsize:v:0 10M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map "[v2out]" -c:v:1 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:1 3M -maxrate:v:1 3M -minrate:v:1 3M -bufsize:v:1 3M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map "[v3out]" -c:v:2 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:2 1M -maxrate:v:2 1M -minrate:v:2 1M -bufsize:v:2 1M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map a:0 -c:a:0 aac -b:a:0 96k -ac 2 \ -map a:0 -c:a:1 aac -b:a:1 96k -ac 2 \ -map a:0 -c:a:2 aac -b:a:2 48k -ac 2 \ -f hls -hls_time 2 -hls_playlist_type vod -hls_flags independent_segments -hls_segment_type mpegts \ -hls_segment_filename landscape_%v/data%02d.ts \ -master_pl_name landscape.m3u8 \ -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" landscape_%v.m3u8
landscape.mp4
into three different resolutions: the original “v1” (1920×1080, in my case, with 96kbit audio), “v2” (1280×720, with
96kbit audio), and “v3” (640×360, with 48kbit audio), each with a resolution-appropriate maximum bitrate, and forced keyframes every 48th frame. Then it breaks each of those into HLS
segments (.ts
files) and references them from a .m3u8
playlist.
The output from this includes:
landscape.m3u8
, which references the other playlists with reference to their resolution and bandwidth, so that browsers can make smart choices,
landscape_0.m3u8
(“v1”), landscape_1.m3u8
(“v2”), etc., each of which references the “parts” of that video,
landscape_0/
, landscape_1/
etc., each of which contain
data00.ts
, data01.ts
, etc.: the actual “chunks” that contain the video segments, which can be downloaded independently by the browser as-needed
We can bring all of that together, then, to produce a variable-aspect, adaptive bitrate, HLS-streamed video player… in pure HTML and suitable for static hosting:
<video controls> <source src="squareish.m3u8" type="application/x-mpegURL" media="(min-aspect-ratio: 0.95) and (max-aspect-ratio: 1.05)" /> <source src="portrait.m3u8" type="application/x-mpegURL" media="(orientation: portrait)" /> <source src="landscape.m3u8" type="application/x-mpegURL" /> </video>
That’ll “just work” in Safari and a handful of mobile browsers… but won’t display anything for most desktop browsers. Boo!
One solution is to also provide the standard .mp4
files as an alternate <source>
, and that’s fine I guess, but you lose the benefit of HLS (and
you have to store yet more files). But there’s a workaround:
If you’re willing to use a JavaScript polyfill, you can make the code above work on virtually any device. I gave this a go, here, by:
// Find all <video>s which have HLS sources: for( hlsVideo of document.querySelectorAll('video:has(source[type="application/x-mpegurl"]), video:has(source[type="vnd.apple.mpegurl"])') ) { // If the browser has native support, do nothing: if( hlsVideo.canPlayType('application/x-mpegurl') || hlsVideo.canPlayType('application/vnd.apple.mpegurl') ) continue; // If hls.js can't help fix that, do nothing: if ( ! Hls.isSupported() ) continue; // Find the best source based on which is the first one to match any applicable CSS media queries const bestSource = Array.from(hlsVideo.querySelectorAll('source')).find(source=>window.matchMedia(source.media).matches) // Use hls.js to attach the best source: const hls = new Hls(); hls.loadSource(bestSource.src); hls.attachMedia(hlsVideo); }
<video>
depend on JavaScript, but if that’s the route you want to go down while we wait for HLS support to become
more widespread (rather than adding different-typed sources) then that’s fine, I guess.
This was a fun dive into some technologies I’ve not had the chance to try before. A fringe benefit of being a generalist full-stack developer is that when you’re “between jobs” you get to play with all the cool things when you’re brushing up your skills before your next big challenge!
(Incidentally: if you think you might be looking to employ somebody like me, my CV is over there!)
“Trans former judge plans to challenge gender ruling at European court”: https://www.bbc.co.uk/news/articles/c9qw2149yelo
Representation matters. That we have a trans former-judge, somebody both well-equipped and motivated to escalate this important challenge to the ECHR, is hugely fortunate.
We need more representation (of trans people specifically, but many other groups too, and perhaps particularly in the intersections) in positions of power, expertise, and authority. To defend the human rights of all of us.
Wishing you luck, Victoria McCloud.
This checkin to GC5FGMC R65: Hazelborough 1: Abby's woofday cache reflects a geocaching.com log entry. See more of Dan's cache logs.
Found after a brief hunt with the help of a boy and his dog. Definitely needed the hint! TFTC!
This checkin to GC9KWV4 Hazelborough trail reflects a geocaching.com log entry. See more of Dan's cache logs.
Found after an extended hunt with the younger child.
A tough search: we were glad of the CO’s recent comment to reassure us it’s still there! SL, TFTC!
I’m keeping an eye out for my next career move (want to hire me?). Off the back of that I’ve been brushing up on the kinds of skills that I might be asked to showcase in any kind of “tech test”.
Not the kind of stuff I can do with one hand tied behind my back1, but the things for which I’d enjoy feeling a little more-confident2. Stuff that’s on my CV that I’ve done and can do, but where I’d like to check before somebody asks me about it in an interview.
LinkedIn, GlassDoor, and bits of the Fediverse are a gold mine for the kinds of things that people are being asked to demonstrate in tech tests these days. Like this post:
My recent React experience has mostly involved Gutenberg blocks and WordPress theme component. This seemed like an excuse to check that I can wrangle a non-WordPress React stack.
Half an hour later, I’d proven to myself that yes, I could throw together a fresh application with React DOM and implement some React components, pass state around and whatnot.
Time to move on to the next thing, right? That’s what a normal person would do.
But that’s not the kind of person I am.
What I found myself thinking was… man, this is chunky. React is… not the right tool for this job.
(Or, increasingly, any job. But I’ll get back to that.)
A minified production build of my new component and its dependencies came in at 202kB (62.3kB compressed). That feels pretty massive for something that does so-little. So as an experiment, I re-implemented my new React component as a vanilla JS Web Component using a custom element. Identical functionality, but no third-party library dependencies. Here’s what I got:
The Web Component version of this control has no dependency chain and uses no JSX, and so it has no transpilation step: the source version is production-ready. You could minify it, but modern HTTP compression makes the impact of that negligible anyway: the whole thing weighs in at 19.5kB (5.2kB compressed) without minification.
And while I appreciate of course that there’s much more to JavaScript complexity and performance than file sizes… and beyond that I appreciate that there’s a lot more to making great components than the resulting bundle size… it’s hard to argue that delivering the same functionality (and less fragility) in a twelfth of the payload isn’t significant.
But there’s a bigger point here:
I’m alarmed by the fact that I’m still seeing job ads for “React developers”, with little more requirement than an ability to “implement things in React”.
From where I’m sitting, React is the new jQuery. It:
document.querySelectorAll
$.ajax
until the Fetch API was a reliable replacement to XMLHttpRequest
If you’ve got a legacy codebase with lots of React in it, you’re still going to need React for a while. Just like how you’re likely to continue to need jQuery for a while until you can tidy up all those edge-cases where you’re using it.
(You might even be locked-in to using both React and jQuery for some time, if say you’ve got a plugin architecture that demands backwards-compatibility: I’m looking at you, WordPress!)
But just as you’re already (hopefully) working to slowly extricate your codebases from any now-unnecessary jQuery dependencies they have… you should be working on an exit plan for your React code, too. It’s done its time; it’s served its purpose: now it’s just a redundant dependency making your bundles cumbersome and harder to debug.
Everything React gives you on the client-side – components, state/hooks, routing4, etc. – is possible (and easy) in modern JavaScript supported in all major browsers. And if you still really want an abstraction layer, there are plenty of options (and they’re all a lot lighter than React!).
The bottom line is, I suppose…
If you’re building a brand new project, you shouldn’t be using React. It should be considered deprecated.
If you’ve got an existing product that depends on React… you should be thinking about how you’ll phase it out over time. And with that in mind, you want to be hiring versatile developers. They’ll benefit from some experience with React, sure, but unless they can also implement for the modern Web of tomorrow, they’ll just code you deeper into your dependency on React.
It’s time you started recruiting “Front-End Developers (React experience a plus)”. Show some long-term thinking! Or else the Web is going to move on without you, and in 5-10 years you’ll struggle to recruit people to maintain your crumbling stack.
You can download all my code and try it for yourself, if you like. The README has lots more information/spicy rants, and the whole thing’s under a public domain license so you can do whatever you like with it.
1 Exploiting or patching an injection vulnerability, optimising an SQL query, implementing a WordPress plugin, constructing a CircleCI buildchain, expanding test coverage over a Rubygem, performing an accessibility audit of a web application, extending a set of high-performance PHP-backed REST endpoints, etc. are all – I’d hope! – firmly in the “hold my beer” category of tech test skills I’d ace, for example. But no two tech stacks are exactly alike, so it’s possible that I’ll want to brush up on some of the adjacent technologies that are in the “I can do it, but I might need to hit the docs pages” category.
2 It’s actually refreshing to be learning and revising! I’ve long held that I should learn a new programming language or framework every year or two to stay fresh and to keep abreast of what’s going on in world. I can’t keep up with every single new front-end JavaScript framework any more (and I’m not sure I’d want to!)! But in the same way as being multilingual helps unlock pathways to more-creative thought and expression even if you’re only working in your native tongue, learning new programming languages gives you a more-objective appreciation of the strengths and weaknesses of what you use day-to-day. tl;dr: if you haven’t written anything in a “new” (to you) programming language for over a year, you probably should.
3 What do job titles even mean, any more? 😂 A problem I increasingly find is that I don’t know how to describe what I do, because with 25+ years of building stuff for the Web, I can use (and have used!) most of the popular stacks, and could probably learn a new one without too much difficulty. Did I mention I’m thinking about my next role? If you think we might “click”, I’d love to hear from you…
4 Though if you’re doing routing only on the client-side, I already hate you. Consider for example the SlimJS documentation which becomes completely unusable if a third-party JavaScript CDN fails: that’s pretty fragile!
Remember that hilarious letter I got from British Gas a in 2023 where they messed-up my name? This is like that… but much, much worse.
Today, Ruth and JTA received a letter. It told them about an upcoming change to the agreement of their (shared, presumably) Halifax credit card.
Except… they don’t have a shared Halifax credit card. Could it be a scam? Some sort of phishing attempt, maybe, or perhaps somebody taking out a credit card in their names?
I happened to be in earshot and asked to take a look at the letter, and was surprised to discover that all of the other details – the last four digits of the card, the credit limit, etc. – all matched my Halifax credit card.
I spent a little over half an hour on the phone with Halifax, speaking to two different advisors, who couldn’t fathom what had happened or how. My credit card is not (and has never been) a joint credit card, and the only financial connection I have to Ruth and JTA is that I share a mortgage with them. My guess is that some person or computer at Halifax tried to join-the-dots from the mortgage outwards and re-assigned my credit card to them, instead?
Eventually I had to leave to run an errand, so I gave up on the phone call and raised a complaint with Halifax in writing. They’ve promised to respond within… eight weeks. Just brilliant.
I’ve had a few breakups, but I’ve only been made redundant once. There’s a surprising overlap between the two…
And with that, I’d better get back to it. Today’s mission is to finish checking-in on my list of “companies I’ve always admired and thought I should work for” and see if any of them are actively looking for somebody like me!
(Incidentally: if you’re into open source, empowering the Web, and making the world a better place, my CV is over here. I’m a senior/principal full-stack engineer with a tonne of experience in some radically diverse fields, and if you think we’d be a good match then I’d love to chat!)
This checkin to GCAR5HV Ivy reflects a geocaching.com log entry. See more of Dan's cache logs.
The (long) log for Mossy describes the adventure to this geocache, too. Go read about it there.