Skip to content
70 changes: 70 additions & 0 deletions src/components/users.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
interface Props {
// Grid columns, since the reading column is too narrow for four across.
class?: string;
// Only show users of this use case.
useCase?: string;
}

const { class: columns = "sm:grid-cols-2", useCase } = Astro.props;

// Companies running MoQ in production. A null `name` renders as a redaction bar
// for users who can't be named yet; `redacted` sets the bar's width.
const users = [
{
name: "Meta",
useCase: "contribution",
what: "Live ingest",
body: 'Spoke about "Shipping MoQ for Instagram and Facebook Live Ingest" at DEMOQED. Uses its own implementation of the standard, not ours.',
links: [{ text: "DEMOQED program", href: "https://moqalliance.org/demoqed-2026/" }],
},
{
name: "Saronic",
useCase: "robotics",
what: "Autonomous boats",
body: "MoQ is the primary video system across its autonomous vessels. Transmits multiple cameras/qualities over Starlink.",
links: [
{ text: "Proof that I'm not lying", href: "https://medium.com/saronic-technologies/the-ground-moves-building-on-early-open-source-without-getting-burned-ba2af78beb78" },
{ text: "Corsair rescues two Army pilots", href: "https://www.navalnews.com/naval-news/2026/06/saronic-usv-rescues-two-u-s-army-pilots-downed-by-iran/" },
Comment thread
coderabbitai[bot] marked this conversation as resolved.
],
},
{
name: "Evolve Robotics",
useCase: "robotics",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
what: "Teleoperation",
body: 'Using MoQ extensively for VR teleoperation. Spoke at Demoqed about their usage.',
links: [{ text: "evolverobotics.com", href: "https://evolverobotics.com/" }],
},
{ name: null, useCase: "robotics", redacted: "w-52", what: "Drones", body: "A major government contractor flying drone around with MoQ (cellular). That's all we're allowed to say.", links: [] },
{ name: null, useCase: "robotics", redacted: "w-44", what: "Drones", body: "Another defense contractor flying drones around, this time with Starlink. The drone controls are also transmitted via MoQ.", links: [] },
];
---

<div class={`not-prose grid gap-5 ${columns}`}>
{
users
.filter((user) => !useCase || user.useCase === useCase)
.map((user) => (
<div class="card flex flex-col gap-2 p-5">
<span class="label">{user.what}</span>
{user.name ? (
<h3 class="text-[1.2rem] font-semibold text-white">{user.name}</h3>
) : (
<h3 class={`my-0.5 flex h-8 ${user.redacted} -rotate-1 items-center justify-center bg-black font-mono text-base tracking-[0.3em] text-white`}>
REDACTED
</h3>
)}
<p class="text-[0.9rem] leading-relaxed text-slate-400">{user.body}</p>
{user.links.length > 0 && (
<div class="mt-auto flex flex-col gap-1 pt-2 text-[0.9rem]">
{user.links.map((link) => (
<a href={link.href} class="green-link">
{link.text}
</a>
))}
</div>
)}
</div>
))
}
</div>
29 changes: 25 additions & 4 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import Users from "@/components/users.astro";
import Layout from "@/layouts/global.astro";

interface Post {
Expand Down Expand Up @@ -37,9 +38,13 @@ const languages = [
{ name: "C", icon: "c", href: "https://doc.moq.dev/lib/c/" },
];

// A few members of the MoQ Alliance. Most build their own MoQ stack, so they
// sit apart from the users above.
const alliance = ["Cloudflare", "Akamai", "AWS", "Fastly", "Cisco", "Intel", "Oracle", "Mux"];

// `icon` is hand-drawn art in public/icons/use-case/.
const useCases = [
{ title: "Robotics", icon: "robotics", body: "Real-time teleoperation, with telemetry and control on the same relays as the video.", href: "/blog/on-a-boat" },
{ title: "Robotics", icon: "robotics", body: "Real-time teleoperation, with telemetry and control on the same relays as the video.", href: "/robotics" },
{ title: "Voice and video AI", icon: "voice-video-ai", body: "Server-side media in Rust or Python, played back faster than real time in the browser.", href: "https://doc.moq.dev/concept/use-case/ai" },
{ title: "Interactive", icon: "interactive", body: "Media down, input up. MoQ Boy is a Game Boy played by everyone watching at once.", href: "/boy" },
{ title: "Conferencing", icon: "conferencing", body: "Browser publish and watch with a live roster, plus WebRTC for WHIP and WHEP clients.", href: "/blog/first-app" },
Expand All @@ -53,11 +58,11 @@ const useCases = [
<section class="mx-auto grid max-w-6xl items-center gap-14 px-5 pb-16 pt-12 md:px-8 lg:grid-cols-[1.1fr_1fr] lg:pt-16">
<div>
<a
href="https://moqalliance.org/demoqed-2026/"
href="/robotics"
class="group mb-7 inline-flex items-center gap-3 rounded-full border border-slate-700 bg-slate-800/60 py-1.5 pl-1.5 pr-4 text-[0.85rem] text-slate-300 transition-colors hover:border-slate-500"
>
<span class="shrink-0 whitespace-nowrap rounded-full bg-green-500 px-2.5 py-0.5 font-mono text-[0.68rem] font-medium uppercase tracking-wider text-slate-900">Oct 8</span>
<span>DEMOQED, the first MoQ conference, in San Francisco</span>
<span class="shrink-0 whitespace-nowrap rounded-full bg-green-500 px-2.5 py-0.5 font-mono text-[0.68rem] font-medium uppercase tracking-wider text-slate-900">In prod</span>
<span>Saronic ships MoQ on its autonomous boats</span>
<span class="text-slate-500 transition-transform group-hover:translate-x-0.5" aria-hidden="true">→</span>
</a>

Expand Down Expand Up @@ -182,6 +187,22 @@ const useCases = [
</div>
</section>

<!-- Users -->
<section class="mx-auto max-w-6xl px-5 pb-16 md:px-8 md:pb-20">
<div class="mb-10 flex items-baseline justify-between gap-6">
<h2 class="display text-[2.1rem] md:text-[2.6rem]">Already in the <span class="scribble">wild.</span></h2>
<a href="mailto:me@kixel.me" class="green-link shrink-0 font-medium">Using MoQ? Tell us</a>
</div>
<Users class="sm:grid-cols-2 lg:grid-cols-5" />
<div class="mt-10 flex flex-col gap-4 sm:flex-row sm:items-center">
<h3 class="shrink-0 font-semibold text-white">Building the standard with</h3>
<div class="flex flex-wrap items-center gap-x-5 gap-y-2 text-[0.95rem] text-slate-400">
{alliance.map((name) => <span>{name}</span>)}
<a href="https://moqalliance.org/" class="green-link font-medium">and the rest of the MoQ Alliance</a>
</div>
</div>
</section>

<!-- Production -->
<section class="border-t border-slate-800 bg-slate-800/30">
<div class="mx-auto max-w-6xl px-5 py-16 md:px-8 md:py-20">
Expand Down
122 changes: 122 additions & 0 deletions src/pages/robotics.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
layout: "@/layouts/global.astro"
title: Robotics
description: MoQ streams video, telemetry, and control for robots on terrible networks.
---

import Users from "@/components/users.astro";

# Robotics

Robots have a lot of cameras.
They're basically cameras with legs or wheels or pinchers or spinning blades or you get the idea.

Teleoperation is becoming less and less relevant, but *something* still needs to monitor the little guys.
A human, a fleet dashboard, an AI model looking for trouble, a recorder for the post-mortem.
And they're all watching over the worst network you've ever seen.

<figure>
![nine cameras on a boat](/blog/on-a-boat/kraken.png)
<figcaption>Arr, there be a kraken on the starboard bow.</figcaption>
</figure>

## Pull, not Push
Existing contribution protocols (ex. WebRTC, SRT, RTMP) are push-based.
The robot constantly transmits *everything* to a server, watched or not.
Not great when you've got 9 cameras and StarLink is expensive.

**MoQ is pull-based.**
Each camera is a broadcast, split into tracks like `1080p`, `360p`, `audio` and `telemetry`.
Nothing is transmitted until somebody subscribes to a track.

So by default, an AI model subscribes to the 360p track from every camera.
If it spots a kraken, then it subscribes to the 1080p track from that camera.
Camera 7 doesn't touch the uplink until somebody cares about camera 7.

## Shit Networks
Robots tend to be somewhere with a terrible network: cellular, satellite, mesh radio, or nothing at all.
Your bandwidth will fluctuate, and *something* has to give.

**You decide what gives.**
Every subscription has a priority, and they all cooperate over one QUIC connection.
Controls before audio before telemetry before the kraken cam before the rest of the cameras.
When the link degrades, the least important stuff waits.

And it waits, it doesn't disappear.
Real-time RTP stacks like WebRTC give up on a frame after a short deadline, and it's gone forever.
MoQ sends the newest frames first and keeps the old ones in RAM, up to a TTL.

Each subscriber decides how long it's willing to wait:
- The **operator** waits 100ms, skipping ahead to stay real-time.
- The **AI model** waits a second, because it'd rather see every frame.
- The **recorder** waits 30 seconds, backfilling once the link recovers.

The result is a lossy live feed AND a pristine VOD from the same stream, as long as the link recovers before the recorder's deadline.

<figure>
![Realtime vs Backfill](/blog/cars2/backfill.png)
<figcaption>If I ever own a racing team, it's going to be called The Backfill Boyes</figcaption>
</figure>

## Bonded Pairs
Near the coast? Use cellular *and* satellite.

QUIC supports path migration, so you can switch from WiFi to cellular to satellite without dropping the connection.
There's also a [multi-path extension](https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/) that uses multiple paths at the same time.
It's [behind a feature flag](https://github.com/moq-dev/moq/blob/main/rs/moq-native/Cargo.toml) if you want to give it a spin.

Or go up a layer and open multiple MoQ connections: P2P via [iroh](https://iroh.computer/), a self-hosted relay, a public CDN.
The robot announces its broadcasts on all of them, and every subscription takes the best available path.
Idle connections cost nothing, because nothing flows until somebody subscribes.

<figure>
![bonded pairs](/blog/on-a-boat/multipath.png)
<figcaption>Like kittens, these pairs are bonded.</figcaption>
</figure>

## Fan Out
If 100 viewers subscribe to the same track, [moq-relay](https://doc.moq.dev/bin/relay/) merges them into a single upstream subscription.
The robot serves *at most* one copy of each track, no matter how many people are watching.

Relays connect to each other and form a [cluster](https://doc.moq.dev/bin/relay/cluster/), handling discovery, routing, and failover.
Self-host a relay on the robot or the base station to keep everything on your network.
Then cluster it with a public CDN like [moq.pro](https://moq.pro) or [Cloudflare](/blog/first-cdn) when the whole world wants to watch.

<figure>
![relay fanout](/blog/on-a-boat/fanout.png)
<figcaption>One uplink from the boat, any number of viewers.</figcaption>
</figure>

## Not Just Media
There's nothing special about media.
It's just timestamped, delta-encoded data, and so is everything else a robot produces.

- The camera? **A track.**
- The GPS? **A track.**
- The joystick? **A track.**
- The killswitch? **A track**, and every robot in the fleet can subscribe to it.

Stamp it all with the same clock and send it over the same connection, so you can put humpty back together on the other side.
WebRTC botches this: data channels have their own congestion control that fights the media instead of cooperating with it.

Controls work the same way, just backwards.
The operator publishes a `controls` track, and the robot subscribes to it.
Lockout, handoff, or anarchy is your application's call, not the relay's.
The [MoQ Boy](/blog/moq-boy) demo does exactly this, with a GameBoy standing in for the robot.

<figure>
![MoQ Boy](/blog/moq-boy/moqboy.png)
<figcaption>weapon of mass destruction?</figcaption>
</figure>

## In Production

<Users useCase="robotics" />

## Yours?

That's only the companies willing to tell me they're using the moq.dev libraries.
Running MoQ on something that moves? [Tell us](mailto:me@kixel.me), we'd love to brag about it.
Or don't, and we'll add another black bar.

Want the long version? Read [On a Boat](/blog/on-a-boat) and the [Cars 2](/blog/cars2) post.
Loading