Embedding the sweep

A versioned contract, like the feed.
Everything on the scope is live data — except the rotation.
Each blip is a real published item from the last seven days: sector is the channel, distance from the centre is age, size and colour are severity. The sweeping arm turns once every seven seconds and means nothing; the pipeline's real position in its six-hour cycle is the thin arc on the outer rim, labelled in hours. Those are two different things on purpose, because pretending a seven-second animation is a six-hour cadence is the one dishonesty this component could get away with. If interpretation is degraded the arm turns amber, stutters, the HUD says so in words, and the published history keeps what was published but states the backlog beside it; if a source has been silent for more than seven days its whole sector dims. There is no state in which this renders "fine" while the pipeline is not.

THE EMBED

<iframe
  src="https://radar.trulysovereign.com/embed/sweep?theme=transparent&compact=1"
  width="480" height="429"
  style="border:0;background:transparent"
  loading="lazy"
  title="RADAR live scope"></iframe>
The scope is 1.12:1, so height ≈ width ÷ 1.12. It fills whatever box you give it; only the aspect ratio matters. This changed in WP23 — it was 1.28:1 while the scope was a tilted disc. If your iframe has a fixed height, widen it or the scope will letterbox inside your frame.

PARAMETERS

themedark · light · transparent
compact1 drops the status chips and the published history
transparent paints no background, so the host page's own background shows through. Use it unless you want RADAR's panel colour.

It needs a dark host. The blips, the sweep trail and the glow are drawn with additive blending, which is what makes them look like light rather than paint — and additive light on a white background is invisible. On a pale page use theme=dark and let the scope bring its own panel.

USING IT DIRECTLY

Prefer the iframe: it isolates CSS and cannot be broken by the host page. If you are building the host yourself and want it inline, load the bundle and mount it:
<div id="sweep"></div>
<script src="https://radar.trulysovereign.com/assets/sweep.js"></script>
<script>
  mountSweep(document.getElementById('sweep'), {
    dataUrl: 'https://radar.trulysovereign.com/data.json',
    theme:   'transparent',
    compact: true
  });
</script>
mountSweep returns { destroy(), reload() }. Call destroy() when you unmount, or the animation loop outlives the element; reload() re-reads the feed.

WHAT IT WILL NOT DO TO YOUR PAGE

It stops when nobody is looking. One requestAnimationFrame loop, paused on visibilitychange and by an IntersectionObserver when scrolled out of view.

It respects reduced motion. Under prefers-reduced-motion: reduce there is no rotation and no afterglow — one static frame, with the published history filled and every item still reachable.

It is keyboard reachable. The canvas is aria-hidden, and every item in the window is a real <a> — in the published history, and in a visually hidden list ordered by severity then recency. That list is the accessible scope, not a description of it.

It is small. One request for the bundle, one for data.json, and nothing else — no fonts, no framework, no tracking.

It says when it cannot check. If the feed is unreadable it says so rather than drawing an empty radar, because an empty radar claims the pipeline found nothing, which is a different and much worse statement.

JUST THE LATEST FIVE ITEMS

The scope is a picture of the pipeline. If what you want is a list — a sidebar, a status page, an internal wiki — this reads data.json directly and needs nothing from RADAR but the one request. No bundle, no iframe, no framework. Paste it into a plain .html file and open it.
<div id="radar-latest">Loading RADAR…</div>
<script>
fetch('https://radar.trulysovereign.com/feeds/v1/all.json')
  .then(function (r) { return r.json(); })
  .then(function (d) {
    var el = document.getElementById('radar-latest');
    var items = (d.items || []).slice(0, 5);
    if (!items.length) { el.textContent = 'RADAR has published nothing yet.'; return; }
    el.innerHTML =
      '<ul style="margin:0;padding-left:1.1em">' +
      items.map(function (i) {
        var t = new Date(i.published_at).toISOString().slice(0, 16).replace('T', ' ');
        return '<li>' +
          (i.corrected_at ? '<b>[CORRECTED]</b> ' : '') +
          '<a href="' + i.url + '">' + i.headline + '</a>' +
          '<br><small>' + i.source_name + ' · ' + t + ' UTC</small></li>';
      }).join('') +
      '</ul><p><small>Source: <a href="https://radar.trulysovereign.com">RADAR</a></small></p>';
  })
  .catch(function () {
    document.getElementById('radar-latest').textContent =
      'RADAR could not be reached. This is a fetch failure, not a report that nothing changed.';
  });
</script>
Four things in there are deliberate and worth keeping if you rewrite it.

It reads /feeds/v1/all.json, not data.json. v1 is the versioned contract, and — the reason this snippet was wrong the first time it was written — its url is absolute. data.json carries site-relative paths like /items/<id>, which on your page resolve against your domain and 404.

i.url is the item permalink, which is what the terms ask you to link to — not the source's own page, so a reader can see what RADAR said and whether it was later corrected.

A corrected item is labelled, not dropped. RADAR withdraws claims in place; a list that silently omits them shows a reader five items that all look current when one has been retracted.

The attribution is in the snippet, not in the instructions, because attribution that has to be added by hand does not survive a copy-paste. And the failure case says the fetch failed instead of rendering an empty list, because an empty list claims RADAR found nothing, which is a different and much worse statement.

Times are absolute UTC. Every timestamp RADAR publishes is, so "2 hours ago" never goes stale on a cached page.

VERSIONING

Not breaking: visual refinement, new optional parameters, new data driving an existing visual.

Breaking, new path: removing a parameter, changing what a visual means, or changing the required host markup. WP20b replaced the SVG sweep with this canvas scope: the parameters, the host markup and the meaning of every mark are unchanged, so by this rule it is a refinement and stays on this URL. Those that are not go to /embed/v2/sweep and this URL keeps working.

A URL someone has put in their HTML is a promise. That it is a promise to a sister project makes it easier to break by accident, not harder — which is the reason it is written here.

The feed contract ↗ · Terms of use ↗ · ← back to RADAR