Skip to content
All field notes

3 min read

The Flash Years

A retrospective on keeping a live auction platform alive through the death of Flash — including the Chrome extension whose only job was turning Flash back on

  • Flash
  • WebRTC
  • Angular
  • Video Streaming
  • Legacy Systems

Before CattleUSA's auction viewer ran on WebRTC, it ran on Flash. This is a retrospective on the 2017–2021 stretch where we kept a real-money livestock auction platform alive while the web platform deprecated our video stack out from under us. None of it is best practice. All of it shipped.

An Angular app driving a Flash player

The UI generation before our current one was an Angular app (Angular 4, eventually 7) whose live-video component built an <object type="application/x-shockwave-flash"> tag as a string and injected it with innerHTML. The flashvars carried the sale barn ID, the buyer ID, and a cache-busting timestamp; the stream came in over RTMP. Flash ignores CSS, so the component hand-computed a 4:3 box against the window height and re-injected the player on 200ms timers when the layout changed — every method wrapped in a change-detector-destroyed guard because it all ran outside Angular's zone.

That app had another distinguishing feature: most of it was generated. A 61-row "app states" spreadsheet was exported to XML and run through an XSLT stylesheet that emitted the module, base component, template, and styles for 68 of its 71 Angular modules, leaving one human-editable stub per screen. The commit that completed the setup is titled "100% derived code now." The cost showed up later, in commits about back-porting base-class changes into the customized stubs — and the next UI generation removed the whole mechanism in a commit named "drop app_states." I learned a durable lesson from that arc: codegen is a loan against your future flexibility, and the interest rate is the diff between what you generated and what you've since hand-edited.

The Chrome extension whose only job was turning Flash back on

In 2019, Chrome started blocking Flash by default. Our auctioneers and buyers were not going to navigate chrome://settings/content mid-sale. So we shipped a Chrome extension whose entire function was one API call: chrome.contentSettings.plugins.set — allow Flash on cattleusa.com. A browser extension as a life-support machine for a deprecated plugin. It bought us the two years we needed.

Meanwhile the new UI had to coexist with the old player: commits from that era read "embed legacy viewer", "flash fallback on IE & Edge", "flash isn't reliably reloading, full SPA reload on reconnect". The replacement wasn't a cutover — the old UI's branch kept receiving commits for months after the new one launched. Migrations of load-bearing systems are overlaps, not switches.

WebRTC work started in late 2019, and 2020 — with sale barns suddenly depending on remote bidders — became a live tuning exercise against real barns on real rural internet. The commit log over about ten days in June 2020 is a bisection in public: 960×540, then 640×480 at 15fps, then 640×360 at 800kbps, then 1000kbps, then 720×405 at 12fps, then 1200kbps, then 8fps, then back to 12fps with contentHint: 'motion' on the video track, plus a fallback to unconstrained audio/video when a device's camera rejected our constraints.

There is no benchmark for "auctioneer's uplink in a metal building sixty miles from the nearest city." You ship a setting, you watch a sale, you adjust. In parallel ran the browser-compatibility trench war: array polyfills for IE, pulling arrow functions out of the index page so IE could even parse it, and a routing rule that sent clients without RTCPeerConnection back to the Flash player. Our customers ran the browsers they ran; "update your browser" is not a support answer during a live sale.

In October 2020 we moved the WebRTC stack to Millicast, which still carries our auction video today. And on August 6, 2021 — four months into Flash's official afterlife — a commit landed with the title we'd been working toward for two years: "no fallback to flash."

What I keep from those years

  • Production constraints beat architectural taste. Old browsers and rural bandwidth shaped that system more than any framework decision.
  • Deprecations are schedules, not events. We ran old and new video stacks side by side for two years, with routing rules deciding per-client. The extension, the fallbacks, the coexistence commits — that's what a migration actually looks like at auction-floor stakes.
  • Every "temporary" bridge needs a demolition date. The Flash fallback outlived its usefulness by months because removing it was nobody's feature. The commit deleting it felt better than most launches.