Demonstrations

Watch it keep working while everything changes.

FrogNet is engineered so that changing conditions are ordinary, not exceptional. The best way to see that isn't a benchmark — it's watching the system stay up while the link starves, a node reboots, or two networks collide. Every demo below reads two ways.

Proof It Works — the 900 MHz call: 1280×720 at 22 fps, bidirectional, over a 1 Mbit ceiling, did/would counter on screen. One correction: the savings figure is said four ways in thirty seconds — 89% saved is roughly 9×, not 6×; the counter in frame is the number of record.

Bandwidth Experiments — the link driven down by hand: 987 → 637 → 450 Kbps, audio holding, then back to 850 at L7 and 17.5 fps.

Communicator and Shared Memory — a program that reads and writes the shared store.

ClaimEvidence
Globally distributed real-time controlDan's lamp — New York actuator, Seattle database, 2,400 miles, 17 April 2026. Externally hosted, an external witness demonstration.
Mobile topology on a real radioDan's 900 MHz drive through Queens — trunk-mounted node, no dropped link across multipath and building occlusion.
Degraded-link adaptive communicationThe 900 MHz call/degradation proof, and the Communicator ladder demo — 1080 refused down to 160×120 and the climb back.
New York to Seattle to New York — 17 April 2026
New York to Seattle to New York — 17 April 2026
The eight rungs of the media ladder
The eight rungs of the media ladder
Keyframe segments with audio interleaved
Keyframe segments with audio interleaved
Send-or-drop at the socket
Send-or-drop at the socket
What you see — the outcome, in plain termsUnder the hood — what the engineering is doing

Everything below is one instance of the programming surface — the coordination in each demo is shared memory, not messages.

The two flagship captures below are the real first-run experiments — recorded live, unedited. The rest land as they're captured.

§01The flagship

The conversation never stopped.

A live HD call held together over a 900 MHz radio while the link was deliberately starved. The headline isn't the resolution — it's that nobody ever redialed. What you're watching is the actual first run, recorded as it happened.

Demonstration 01 · bandwidth + jitter

HD, then audio, then a heartbeat — and back.

What you see: a live HD video call — 1280×720 at 22 frames a second — running across a 900 MHz radio with a one-megabit ceiling. Squeeze the link and it steps down the ladder (987 → 637 → 450 kbps) with audio holding priority as the picture thins; ease off and it climbs back to L7 and full frame rate, one probed rung at a time. No reconnect, no redial — and an on-screen counter shows ~89% of the bytes never had to be sent.

Under the hoodSotF's quality ladder steps fidelity to match the bearer in real time and recovers in-session. Jitter and loss are injected on purpose, not just to prove survival but to map the operating region where bandwidth, latency, jitter, and loss interact.
Engineering facts
Transport path
Wi-FiWireGuardInternetWireGuard900 MHz
Link
900 MHz radio, ~1 Mbit ceiling
Baseline run
1280×720, 22 fps, no drops, bidirectional, no delay — first run over 900 MHz, a different session from the ladder walk below
Bandwidth saved
~89% (did-vs-would counter) — roughly 9×
Impairments
bandwidth reductionjitter injection
Observed ladder
987 kbps637 kbps · L6450 kbps · L5audio priority850 kbps · L7 · 17.5 fps (ladder walk, on the climb back)
Operator action
A slider — no reconnect, no redial; recovers on its own

The path it ran over — one heterogeneous route, coast to coast

SeattleLaptop
nodeSeattle FrogNet
rendezvousBroker
nodeNew York FrogNet
node2nd NY node
New YorkLaptop
Why the path matters

Wired Wi-Fi, an encrypted internet tunnel, and a 900 MHz radio — all in one call. The adaptation responds to the characteristics of the link (bandwidth, latency, jitter, loss), not to any specific medium. That is what "transport independent" means, shown rather than claimed.

The companion capture — establishing the baseline

"Proof it works" — the steady-state run. HD video holding over the 900 MHz link before any degradation, with the did-vs-would byte counter reading ~89% saved. This is the baseline the ladder climbs down from.

What this one proves

UnREST, in one sentence you can watch

What you see: a bidirectional 1280×720 call at 22 fps on a one-megabit radio, with no four-second delay — because the wire carries only the audio and video themselves, not the REST framing and control traffic that normally rides along. Strip the messages and the picture fits where it never fit before.

"I never thought I'd be able to do this level of video at this bandwidth."

— from the live capture, 900 MHz run
§02The rest of the sequence

Four more that make the same point.

Different failure, same behavior: the system treats the disruption as routine and keeps the thing you care about running.

Figures below are read off the narrated runs of 25 and 30 July, on the network as it was hooked together that day — not a tuned rig. Two of the four captures are still to be recorded; the numbers are already in hand. Run frognet_monitor and pipe_workload on your own pond and you will get your own.

Demo 02 · network mergedrop in capture
Demonstration 02 · merge

The network healed itself.

What you see: two separate little networks come into range of each other and, without anyone configuring anything, become one — everybody can suddenly reach everybody.

Under the hoodEvery node runs an HTTP interface that answers three questions about itself — who are you, who do you know, what is your default route. runmerge asks its directly attached neighbours, then their children, then their grandchildren, installing a route for each one it didn't already have. Discovery runs entirely on the reserved .2 addresses, so nothing in flight on .1 is disturbed; a .2 route is promoted to .1 only when it has to replace one. The DatabaseHost is re-elected for the combined network as part of the same pass.

"A successful clear pass is the only way to exit the merge process."

— Merge, narrated live · 30 Jul
Engineering facts
Setup
Two independent ponds, no shared config
Trigger
a network event— DHCP, or a neighbour reporting one. It does not poll.
Name resolution
No DNS — no authoritative upstream by design. A coordinated /etc/hosts, complete on every node.
Discovery plane
.2 addresses.1 traffic untouched; promoted only to replace a route
Observed
neighbourschildrengrandchildrenhost re-electionroutes installed
Convergence
Two or three runs across the whole mesh
State carried over
None — every merge is a complete drop and rediscovery from scratch
Operator action
None — no configuration touched
Demo 03 · node rebootdrop in capture
Demonstration 03 · self-healing

A node rebooted. Nobody noticed.

What you see: on the live network monitor, a node drops off the map, restarts, and rejoins — while everything else keeps working the whole time.

Under the hoodfrognet_monitor carries four states — online, degraded, idle, down — and degraded means an RTT past 250 ms, not a guess. The node is marked down to the rest of the network the moment it stops answering, its downstream children go with it, routes re-form around the gap, and the node is re-absorbed by the next merge with no manual step.
Engineering facts
Fault injected
node reboot
Monitor states
onlinedegradedidledown
Degraded means
Round-trip time past 250 ms
Observed
marked downchildren drop with itroutes re-formrestartrediscoveryre-absorbed
Rest of mesh
Kept answering throughout
Operator action
None — self-healed
Demo 04 · echo fan-outdrop in capture
Demonstration 04 · fan-out

One message, a thousand times, almost free.

What you see: the same status echoed out to a whole mesh of nodes, over and over — and the bandwidth graph barely moves.

Under the hoodTwo mechanisms at once. The codec collapses a repeated assertion to a SAME verdict, so it costs a few bytes rather than a payload. And identical requests already in flight are never issued twice — the proxy holds the duplicates, the daemon issues one call to Apache, and every waiting thread is answered from the single reply. Fan-in and fan-out on the same standing socket.

"When we're running FrogNet echoes before, they were all the same. It was all from that echo."

— System Perf, narrated live · 30 Jul
Engineering facts
Workload
pipe_workloadecho300 requests
Direct to Apache
4.6 req/s on 8080 — the engine bypassed
Through the engine
8.6 req/s on port 80 — nearly double, same work
Burst
20.5 req/s to one remote node; 66.5 req/s to another — both across the internet
Codec, this node
97.2% compression99.4% SAME0 errors
Coalesced
707 messages folded into single calls
Wire, DatabaseHost
11 MB saved; 520 bps actual
Demo 05 · mixed workloaddrop in capture
Demonstration 05 · realistic load

Everything at once, still calm.

What you see: chat, live telemetry, and discovery all running together on the same network — and it just behaves.

Under the hoodThe chat workload is deliberately the unkind one: roughly a fifth of it is shaped to be non-repeating, so the codec has to earn its verdicts instead of answering SAME to everything. That is the point of running it — a mix that exercises the whole SAME/DIFF path, with the failures left in.

"We've got same 14, diff 11 — which is good, because that means we're exercising the full semantic compression engine."

— System Perf, narrated live · 30 Jul
Engineering facts
Running at once
chattelemetrydiscovery
Workload shape
~20% deliberately non-repeating, so DIFF is forced
Verdicts
138 SAME145 DIFF17 failed
Wire
20.1 MB saved; 32 kbps effective against 686 bps actually sent
Shared substrate
One FrogNet Memory, concurrent handlers
Open, not hidden
The ramp climbs, then drops back — repeatably. Unexplained.
§The video series · in production

Eight videos, one path — from nothing to a working call.

The series is shot. Every video follows the same beats — claim, experiment, observation, explanation — and every one carries an honesty beat: the thing that failed on camera stays in. The ladder demo runs 1080 down to 160×120 and climbs back; the floor on camera is 164 Kbps with audio holding and no dropped frames — below the hero's 450, which is the point.

The running order, the per-video notes, and the deep dives still to be shot are on the series index. The six mistakes remain their own page — the six ways you will reach for a protocol →

The video series →

The point of all of it

We optimize for the day something goes wrong.

Not for the benchmark on a good day. Every demo shows the same discipline — solve the root cause, design for failure, automate over configure — so that links flapping, nodes vanishing, and networks splitting are ordinary weather, not an outage.

§03What is not built yet

The Communicator is a very complicated Hello, World.

It places real video calls between real machines across the real internet, and it does it in a few thousand lines because the substrate did the hard parts. That is what it is for — read it for the shape. It is not a finished product, and the edges below are written down rather than left to be discovered by surprise. None of them is hidden behind a fallback; each one fails visibly and says what it is. Three of the five limits documented here were closed on 11 August 2026. They stay on the page, marked closed, because the closing is part of the record.

The largest one

Per-client rate adaptation

What should happen. A transmitter sends at the best rate it can manage and does not care how fast anyone receives it. Each client gets its own non-blocking socket, the relay streams to all of them in parallel, and the resolution adapts per leg. A slow participant sees a smaller picture. Nobody else notices they are there.

What actually happens. The relay is a byte fan. It can pass a frame to a viewer or drop it, and that is the entire set of moves available to it — it never decodes anything, which is exactly why it is small and fast and content-blind. So the only way it can serve a constrained client is to drop whole frames, and dropping every third frame of a 1280×720 stream does not produce a smaller picture. It produces a slideshow at full resolution: a low frame rate, no pixellation, and a viewer wondering whether the call has frozen.

What happens instead. Each receiver publishes what it is getting and whether it is keeping up, and every participant derives the call's rate from those rows. The consequence is unchanged — the call still runs at the slowest participant's rate — but it is no longer a misdirected report. It is one stream and one rate by design, bounded by the slowest published report, with the slow end identified by name. That is an architecture, not a bug.

Even fixed, that only shares the pain more fairly. The transmitter would still be sending one stream and the slowest participant would still decide what it is. What it needs is transcoding at the relay — decode once, encode per client at that client's budget, with the ladder applied per leg rather than per call. The per-leg budget already exists and is already enforced; what is missing is the ability to send a client something other than what arrived. That is the difference between a relay that forwards and a relay that serves.

Still open, unchanged

Receive-side audio priority

On the way out, audio takes precedence over video for the shared wire — a voice packet never waits behind a keyframe. On the way in, frames are handled in arrival order. The same rule should apply in both directions and currently does not.

New · found since

The store returns partial results under load

Ask it for everything and, under enough load, it answers with less than everything. The rate loop is deliberately tolerant of that; nothing else is.

New · found since

Capture hardware lies about its limits

One USB camera delivers 1.5 of a wanted 50 samples a second; one platform's camera backend ignores a request for single-frame buffering and queues frames — which arrives downstream looking exactly like a link that stalls and recovers. Both are detected and named rather than mistaken for network trouble. Neither is fixed.

New · found since

Software encode is the ceiling on small hardware

Not the wire. On a small box the encoder runs out before the link does, and no amount of bandwidth buys the frame back.

Closed · 11 Aug 2026

The rung is a sender-side decision

Was: everything the ladder knew, it learned from the sender's own telemetry. Now a receiver publishes what it is getting and whether it is keeping up, and every participant derives the rate from those rows. The receiver is heard directly, not inferred from drop reports.

Closed · 11 Aug 2026

A headless publisher must originate

Was: an unattended camera had to create its own call session. Now a call with nobody selected joins an open call, and a member's own row carries where it connected — so a call survives its originator leaving.

Closed as a mechanism · 11 Aug 2026

Backpressure is reported in aggregate

The relay's aggregate report no longer moves the rate. What remains worth keeping: the relay still cannot name whose frame it discarded — which matters less, because the viewer says so itself.

And the claim this page can now make

The rate control is a pure function of published state.

No clock, no self, no I/O — every participant computes it independently and arrives at the same answer. There is no negotiation in it anywhere. The function is printed in full below; check by eye that there is no message in it.

@staticmethod
def derive_rate(state, ladder, current=None):
    """The call's rate, from the state alone. Pure: no clock, no self, no IO."""
    senders = state.get("senders") or []
    if not senders:
        return None
    slowest = min(senders, key=lambda s: s["w"] * s["h"]
                  * (s["fps"] if s["fps"] > 0 else REF_FPS))
    i = idx_of(slowest)

    getters = state.get("getters") or []
    worst = ceiling_from(getters)          # the smallest size anybody failed at

    if any(g["struggling"] for g in getters):
        i += 1
    elif getters and all(g["happy"] for g in getters):
        i -= 1
    elif not getters:
        i -= 1

    if worst is not None:
        i = max(i, worst + 1)
    return dict(ladder[max(0, min(len(ladder) - 1, i))])

And to answer the question before it is asked: there is no public source repository for the FrogNet reference implementation. The engine is closed and licensed — free to qualified developers, paid for commercial deployment — while the protocol is headed for an open specification. What is public today: the thousand-page build record, the repository and its open Discussions, the simulator, and functions like the one above, printed in full. The implementation is licensed. The argument is not. The licensing plan →

Casual users can install the Communicator and get value from it as it stands. If that is you, there is one thing we would ask in return. Tell us what happened. Feedback is neither positive nor negative; it is actionable or it is noise. "The picture froze when I walked into the kitchen" is worth more than a paragraph of praise and more than a paragraph of complaint, because it names a thing someone can go and look at. What you were doing, what you expected, what happened instead — that is the whole format.

If you would rather fix one than report it, that list is also the map. Per-client rate adaptation is the largest piece on it and a real engineering problem — a relay that serves rather than forwards. The media path is a working group in the guild, open to join with nobody's permission, and somebody will eventually take responsibility for it: whoever does the work, not whoever asks. Run the demonstrations on your own pond and the numbers you get are yours, not mine — if they disagree with these, that is a finding, and I would rather have it from you than not have it.

Much of what is written in the build manual was found exactly that way. The full list is Part XVI — What Is Not Built Yet →

§04Open work · the media stack

One starved leg should not set the level for everyone

We need: media engineers, codec people, anyone who has fought adaptive streaming in the real world.

Say the structural part first, because every reader assumes otherwise: this is not a two-party tool. Each participant uploads one stream to the media host, the host mixes, each participant downloads one mixed stream, so adding participants does not change the bandwidth at any edge. That is what a mixer buys you, and mixers are not new — this is an MCU. What is new is where the mixer lives: mediahost.frognet is an elected role on hardware you own, found through shared memory, and if the box holding it goes away the role floats.

The open piece. A client's send level is set by a tuple, and transmitter and receiver must sit at the same rung. Two-party, that is invisible. Multi-party, it is not: one participant on a starved link sets the level of the mixed stream everyone receives. Three on fibre and one on a bad radio, and all four get the radio's ladder. The fix is host-side transcoding — accept from each transmitter at native resolution and rate, encode per receiver, so a weak leg degrades only itself. The per-leg machinery already exists in shape: sotf_leg_adapt.py is written for exactly this, with per-rung treatment already defined. What is missing is the transcode path in front of it. This is the largest open piece in the media stack.

And one thing to reconcile while you are here. Rung geometry disagrees across three files: fnav.py has L7 1280×720 and L5 640×360 grayscale; sotf_leg_adapt.py scales L7 to 640 wide; call_mix_encoder._RUNG has L7 640×480 and L5 320×240 with no grayscale flag. Client capture and host mix are genuinely different things, but three tables is one too many.

How you would know it worked. A three-party call in the simulator where one leg is starved to the video floor and the other two hold their rung. Fails today, passes on the fix.

None of these is hidden behind a fallback. Each one fails visibly, says what it is, and is written down — which is the only honest way to ship something unfinished. The media seam → · Take the free licence →