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.
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 · transparentcompact1 drops the status chips and the published historytransparent 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
It respects reduced motion. Under
It is keyboard reachable. The canvas is
It is small. One request for the bundle, one for
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.
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
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.
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
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.
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.