Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed public/demo/bunny.png
Binary file not shown.
288 changes: 288 additions & 0 deletions public/demo/bunny.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
537 changes: 537 additions & 0 deletions public/demo/carla.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 88 additions & 0 deletions public/demo/publish.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 29 additions & 4 deletions src/components/voice/cat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ const BLINK_MS = 120;

type Tap = { analyser: AnalyserNode; samples: Float32Array<ArrayBuffer>; spectrum: Float32Array<ArrayBuffer> };

export default function Cat(props: { voice?: AudioNode; class?: string }) {
// With no voice, `babble` makes up speech: bursts of syllables, each a random loudness and
// brightness, through the same lip sync as the real thing. Reduced motion keeps the cat still,
// apart from lip sync to a real voice.
export default function Cat(props: { voice?: AudioNode; babble?: boolean; class?: string }) {
const [shape, setShape] = createSignal<Shape>("rest");
const [blink, setBlink] = createSignal(false);
let tap: Tap | undefined;
Expand Down Expand Up @@ -57,13 +60,34 @@ export default function Cat(props: { voice?: AudioNode; class?: string }) {
let mouthAt = 0;
let blinkAt = last + 2500;
let frame = 0;
const still = matchMedia("(prefers-reduced-motion: reduce)").matches;
const babble = props.babble && !still;
let syllable = { open: 0, tone: 1, closeAt: 0, endAt: 0 };
let syllables = 0;

const tick = (now: number) => {
const dt = now - last;
last = now;

let target = 0;
if (tap) {
let tone = 1;
if (babble && !tap) {
if (now >= syllable.endAt) {
// Between phrases, a breath.
const pause = syllables-- <= 0;
if (pause) syllables = 3 + Math.floor(Math.random() * 8);
const ms = pause ? 700 + Math.random() * 1500 : 140 + Math.random() * 120;
syllable = {
open: pause ? 0 : 0.2 + Math.random() * 0.8,
tone: 0.7 + Math.random() * 0.5,
// Close a little before the next one so syllables read apart.
closeAt: now + ms * 0.65,
endAt: now + ms,
};
}
target = now < syllable.closeAt ? syllable.open : 0;
tone = syllable.tone;
} else if (tap) {
const { analyser, samples, spectrum } = tap;
analyser.getFloatTimeDomainData(samples);
target = level(samples);
Expand All @@ -73,14 +97,15 @@ export default function Cat(props: { voice?: AudioNode; class?: string }) {
bright = bright ? ease(bright, hz, 0.3, dt) : hz;
usual = usual ? ease(usual, hz, 0.02, dt) : hz;
}
if (usual) tone = bright / usual;
}
open = follow(open, target, dt);

if (now - mouthAt >= MOUTH_MS) {
mouthAt = now;
setShape(pick(open, usual ? bright / usual : 1));
setShape(pick(open, tone));
}
setBlink(now >= blinkAt);
setBlink(!still && now >= blinkAt);
if (now >= blinkAt + BLINK_MS) blinkAt = now + 2000 + Math.random() * 4000;

frame = requestAnimationFrame(tick);
Expand Down
10 changes: 6 additions & 4 deletions src/pages/demo.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ title: Demos
description: Live demos of the MoQ protocol in action.
---

import Cat from "@/components/voice/cat";

# Demos

Click on any of them. That's what they're here for. Everything is open source!
Expand All @@ -19,23 +21,23 @@ Click on any of them. That's what they're here for. Everything is open source!

<a href="/car" class="card group flex flex-col gap-4 p-5 text-white">
<div class="flex h-28 items-center justify-center rounded-xl bg-slate-900">
<img src="/icons/use-case/robotics.svg" class="max-h-20 max-w-[70%] object-contain" alt="CARLA" />
<img src="/demo/carla.svg" class="max-h-20 max-w-[70%] object-contain" alt="CARLA" />
</div>
<div class="text-xl font-semibold group-hover:text-green-500">CARLA</div>
<div class="text-sm leading-relaxed text-slate-300">Steer simulated vehicles in CARLA over MoQ. Cameras stream on demand, with controls and telemetry on the same connection.</div>
</a>

<a href="/watch?project=demo&name=bbb.hang" class="card group flex flex-col gap-4 p-5 text-white">
<div class="flex h-28 items-center justify-center rounded-xl bg-slate-900">
<img src="/demo/bunny.png" class="max-h-20 max-w-[70%] object-contain" alt="BBB" />
<img src="/demo/bunny.svg" class="max-h-24 max-w-[70%] object-contain" alt="BBB" />
</div>
<div class="text-xl font-semibold group-hover:text-green-500">B I G B U C K B U N N Y</div>
<div class="text-sm leading-relaxed text-slate-300">Watch a "live" broadcast of everybody's favorite rodent friend. The video player is available as a (tree-shakable) Javascript library or <code>\<moq-watch\></code> web component.</div>
</a>

<a href="/voice" class="card group flex flex-col gap-4 p-5 text-white">
<div class="flex h-28 items-center justify-center rounded-xl bg-slate-900">
<img src="/voice/ai.svg" class="max-h-20 max-w-[70%] object-contain" alt="Voice AI" />
<Cat client:visible babble class="h-24 w-24" />
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
</div>
<div class="text-xl font-semibold group-hover:text-green-500">Voice AI</div>
<div class="text-sm leading-relaxed text-slate-300">Talk to a Pipecat voice agent over MoQ, with a live transcript of both sides. It sends its reply faster than real-time, and you can interrupt it whenever.</div>
Expand All @@ -51,7 +53,7 @@ Click on any of them. That's what they're here for. Everything is open source!

<a href="/publish" class="card group flex flex-col gap-4 p-5 text-white">
<div class="flex h-28 items-center justify-center rounded-xl bg-slate-900">
<img src="/layout/publish.svg" class="max-h-20 max-w-[70%] object-contain" alt="Publish" />
<img src="/demo/publish.svg" class="max-h-20 max-w-[70%] object-contain" alt="Publish" />
</div>
<div class="text-xl font-semibold group-hover:text-green-500">Publish</div>
<div class="text-sm leading-relaxed text-slate-300">Go live! Share your camera, microphone, or screen with anyone you give the watch link to. Browser demos use a private broadcast in the try project.</div>
Expand Down
Loading