What's in it for you / How it's built

How it's built.

A broadcast starts on a pair of glasses in a storm and ends on a screen in seconds. Here is every part it passes through, what each part does, how they talk to each other, and why each one is there.

Written for anyone. If you build things, open Engineering detail under any part.

01 · The map

One picture of the whole system.

Video goes from a camera to Cloudflare and from Cloudflare to viewers. Everything else, the apps, the API and the edge workers, decides who may do what, keeps the records, and tells people what changed.

How OutThere is builtCaptureAppsCoreCloudflareOutside servicesMeta glassesCamera + micOBS & encodersStream keyMobile appiOS + Android · ExpoWeb appNext.js · VercelAdmin CMSNext.js · VercelBackground jobsInngestAPINestJS · RailwayPostgresRecordsRedisFan-outPlaylist proxyWorker · frame clockCloudflare StreamIngest · record · deliverLanding-map liveWorker + Durable ObjectR2 storageRadar · posters · backupsOverlay compositorContainers · burn-inBasemapProtomapsWeather dataNWS · Open-Meteo · IEMRadar imageryRainViewerPushExpo pushEmailPostmarkDesk alertsSlack

Select any part to see what it does, why it exists, and what it talks to. Or follow a journey to watch one action move through the system, step by step.

  • Shipped
  • Rolling out or in progress
  • In progress
  • Rolling out

02 · The parts

What each part does, and why it’s there.

Capture

Where the picture comes from.

  • Meta glasses

    Camera + mic

    The camera and microphone on the broadcaster’s face. The glasses hand the picture and sound to the phone in their pocket; they never talk to the internet themselves.

    Why. Both hands stay on the wheel, the radio or the tripod, and the picture is the broadcaster’s own eye line.

    Engineering detail

    Meta Wearables Device Access Toolkit (DAT 1.0). The native module expo-glasses-stream takes the frames and feeds an RTMPS publisher: HaishinKit on iOS, RootEncoder on Android. The app shows the glasses’ link state so a dropped connection never reads as live.

  • OBS & encoders

    Stream key

    Any encoder that speaks RTMPS: OBS on a laptop, a hardware encoder, a rig in a truck.

    Why. Crews that already have a rig should not have to give it up. The stream key from the app puts them into the same stream as everyone else.

    Engineering detail

    The API hands the owner the live input’s RTMPS address and key. The encoder publishes straight to Cloudflare Stream; nothing of ours sits in the video path.

Apps

What people hold, and what the desk works in.

  • Mobile app

    iOS + Android · Expo

    Go live from the glasses or the phone camera, tag weather events as they happen, watch what is live with the tags and a map of where it was shot, upload what was shot without signal, and get reminders for scheduled streams.

    Why. One app for broadcasting and watching, so a viewer is one tap from going live. Native code only where the camera and the encoder need it.

    Engineering detail

    Expo host app. Publishing is native: expo-live-rtmp (HaishinKit 2.2 with adaptive bitrate) is shared by the glasses and phone-camera sources. Playback with expo-video; the overlay template runs in a WebView. Uploads use a small hand-written TUS client that reads 5 MiB at a time from disk, because the stock client loads the whole file into memory and a 2 GB video would run a phone out of it. Tokens live in the secure store. The watch-list socket asks for a token on every connect: a WebSocket cannot carry an auth header, and access tokens are short-lived. While live, a five-second location fix and every quick-tag tap wait in small outboxes on the phone and are sent with how long ago they happened, so a retry or a dead zone never moves them in time. The playback map is native maps, placed by the same frame clock as the tags.

  • Web app

    Next.js · Vercel

    Watch pages, the desk, the map, the archive and share links; going live from a browser camera, and uploading from a laptop. Players show weather-event tags and a map of where the camera was.

    Why. The browser does not call the API directly for HTTP. The web app’s own server forwards a fixed list of routes, which keeps the API’s public surface small and the long-lived sign-in token in a cookie that page scripts cannot read.

    Engineering detail

    Next.js 16 App Router as a backend-for-frontend. One catch-all route forwards to the API through an exact-segment allow-list; each entry is marked bearer or public, and public entries never forward credentials. Refresh tokens sit in an httpOnly cookie, and refresh is serialized across tabs with Web Locks because the API rotates refresh tokens and treats a second use as theft. Playback is hls.js (native HLS on Safari) with recovery for the seconds while a stream is starting. Browser go-live publishes over WHIP; uploads use tus-js-client in 5 MiB chunks. The watch-list WebSocket is the one connection that goes to the API directly. The playback map is MapLibre, loaded only when a picture has a track.

  • Admin CMS

    Next.js · Vercel

    The desk’s back office: approve new users, work the safety queue of held streams, videos and photos, review packages, manage streams and videos, curate shelves, stories, tags and the landing map, keep the list of weather events broadcasters can tag, fix tags on any broadcast, see a broadcast’s route, and order burned-in copies.

    Why. Moderation and curation live in their own app, so the public site ships none of that code and none of those routes.

    Engineering detail

    Next.js 16 with Refine and the Nesting admin shell. Calls the API’s admin routes from the browser with an admin’s token; the API’s role check is the real boundary, and the UI check is only convenience. Deleting a video hides it; purging is a separate, deliberate action.

Core

Where decisions are made and records are kept.

  • Background jobs

    Inngest

    Work that has to happen later or on a clock: turning a finished broadcast into a replay, sending reminders, sweeping up, alerting the desk.

    Why. Every step is retried and recorded, and a scheduled job runs once no matter how many copies of the API are up.

    Engineering detail

    Inngest Cloud calls back into the API to run each step. Functions: recording-ready (poll until the replay is ready, save it, then switch the live input off); a reconciler every 30 minutes for stranded streams; a daily reminder claim that pushes to phones; an hourly radar sweep; purges with retries; Slack alerts for go-live and new sign-ups; the approval email; the safety scans (one for streams and videos, one for photos) and a sweep every 10 minutes that retries any scan that stalled.

  • API

    NestJS · Railway

    The source of truth. Accounts, who may see what, the life of every broadcast and video, and the signed tokens that let a player fetch video.

    Why. Video never flows through it. The API decides and records; Cloudflare carries the bytes. That is how a modest server serves a large audience.

    Engineering detail

    NestJS on Fastify, run as a container behind Cloudflare. Product modules: live-stream (lifecycle, Cloudflare live inputs, playback signing, the watch-list socket, Stream webhooks, the reconciler), video-asset (uploads, replays, posters, purge), moderation (holds an unverified account’s public request until an image model has read the pictures, and keeps the desk queue), overlay, live-stream annotations (the five-second location track and weather-event tags), the weather-event list, share links, carousels, stories, tags, landing map, TV-market (DMA) lookup, weather, radar, push and users (approval, roles, follows). In progress: image-asset (photos in R2) and package (bundles of streams, videos, photos and rich-text posts). Better Auth handles sign-in (Google, Apple, email code, magic link, password), and the API mints its own short-lived JWT. Every route sits behind an approved-user guard unless it is marked public; admin routes add a role check. Live playlists are signed locally with RS256 and expire in 15 minutes. Webhook signatures are checked against the raw request bytes.

  • Postgres

    Records

    Every account, stream, video, tag, share link, shelf and story, and every broadcast’s route and weather-event tags.

    Why. Relational data with real constraints, written in one place.

    Engineering detail

    Postgres 16, TypeORM. Schema changes are hand-written SQL migrations applied before a deploy; letting the ORM alter the schema is refused in production. Main tables: user_profile, live_stream, live_stream_log, video_asset, share_link, carousel, story, tag, landing_map_entry, weather_event, live_stream_event_tag, live_stream_location_sample, TV-market boundaries and ZIP centroids, overlay_render. Better Auth keeps its own schema. A nightly backup goes to R2.

  • Redis

    Fan-out

    Memory shared by every copy of the API.

    Why. When one copy of the API sees a stream go live, the viewers connected to every other copy have to hear about it too.

    Engineering detail

    Valkey/Redis: the second cache tier behind an in-process one, the TURN-credential cache, the radar fallback, and a pub/sub channel that relays watch-list changes. The message carries only which row changed; each copy reloads it and re-checks every socket’s access, so a broadcast never reaches a viewer it should not.

Cloudflare

Where video and crowds are handled, close to viewers.

  • Playlist proxy

    Worker · frame clockRolling out

    Sits in front of Cloudflare Stream’s playlists and stamps each video segment with the wall-clock time it was shot.

    Why. The on-picture clock has to show when a frame was captured, through pause, seek and replay. Stream’s playlists do not carry that time, so a small edge worker adds it.

    Engineering detail

    Cloudflare Worker. Rewrites master and media playlists to add EXT-X-PROGRAM-DATE-TIME from the broadcast’s anchor time. A one-second edge cache on live playlists turns any number of viewers into about one upstream fetch a second. It has no auth of its own: the signed token in the URL is the permission, and upstream hosts are allow-listed. The web app carries the same rewriter as a fallback route. Live in the development environment; production cutover is next.

  • Cloudflare Stream

    Ingest · record · deliver

    Takes in every live broadcast, records it, and delivers video to every viewer. Uploads land here too.

    Why. Ingest, transcoding and a global CDN are the hard and expensive part of live video. Buying them means a broadcast reaches viewers in seconds without us running media servers.

    Engineering detail

    One live input per stream, created switched off and switched on at Go live. RTMPS for the glasses, phone and encoders; WHIP for a browser. Recording is on, with a 90-second reconnect window so a dropped cell link does not split one broadcast into several. Viewers play low-latency HLS with signed tokens. Uploads go straight from the device to a one-time TUS address. Two kinds of signed webhook come back to the API: live-input connect and disconnect, and video ready.

  • Landing-map live

    Worker + Durable Object

    Pushes the home-page map, who is live and what the desk is featuring, to every visitor at once.

    Why. The landing page is the busiest public surface. Fanning it out from the edge keeps that crowd off the API.

    Engineering detail

    Cloudflare Worker with one SQLite-backed Durable Object. The API publishes the map state (debounced to about 750 ms) on every public stream change and every CMS edit; the object stores it, sends a snapshot on connect and broadcasts each change. Hibernating WebSockets mean an idle visitor costs nothing; a ping is answered without waking the object, and any other message from a client closes its socket. Out-of-order publishes are dropped, and an alarm re-reads the API when a scheduled change falls due.

  • R2 storage

    Radar · posters · backups

    Object storage for what Stream does not hold: radar frames, public poster images and database backups. Photos for packages are in progress.

    Why. A public link needs a poster image that does not expire, and Stream’s are signed. Radar frames are fetched once and served from the CDN, instead of every browser asking the radar provider.

    Engineering detail

    Radar: the API caches frames to R2 and redirects clients to the CDN copy, with Redis as a fallback and direct fetches as the last resort; a two-day lifecycle rule and an hourly sweep keep it small. Posters are copied from Stream when a replay is saved. The database backup lands here nightly.

  • Overlay compositor

    Containers · burn-inIn progress

    Burns the credit, the place and the time into a copy of a video, for everything that leaves our own players: downloads, shares and partner feeds.

    Why. In our players the readout is drawn live over a clean picture, which adds no delay. Once a file leaves, there is no player to draw it, so the credit has to be in the pixels. The original stays clean, so a better design can re-render old video.

    Engineering detail

    Cloudflare Containers, two microVMs per job. A sealed renderer runs Chromium to draw the overlay template, with no internet and no secrets, because template code is treated as untrusted. An ffmpeg worker reads the clean signed HLS, composites one overlay frame a second, encodes, and uploads the result to a one-time Stream address as a separate video. A Durable Object per session owns start, stop, health checks and a time-limit reaper; a singleton enforces capacity and single-use request nonces. Requests from the API are HMAC-signed with a five-minute window. Renders ordered from the CMS run in development; production and live burn-in for partner feeds come next. An AWS Fargate version is built as a fallback and not deployed.

Outside services

Services we use rather than build.

  • Basemap

    Protomaps

    The streets, towns and terrain under every pin on the map, and under the small map on the picture.

    Why. Hosted vector tiles, styled to match the site and restricted to our own domains.

    Engineering detail

    MapLibre GL with the Protomaps basemap flavors on the web, for the map and for the playback map on the picture. The mobile app uses native maps.

  • Weather data

    NWS · Open-Meteo · IEM

    Current conditions, the next hours, alerts, storm reports, and the town name on the picture.

    Why. Public, authoritative sources. The API calls them once and answers every client, so no app has to.

    Engineering detail

    National Weather Service points, alerts and zones (which also name the overlay’s town and county, rounded to about a kilometer and cached for a day); Open-Meteo for the forecast and place search; the Iowa Environmental Mesonet for storm reports. The TV-market lookup is our own data in Postgres.

  • Radar imagery

    RainViewer

    The radar frames drawn over the map.

    Why. The provider limits requests per visitor. Fetching each frame once in the API and serving it from R2 means a crowd costs the same as one viewer.

    Engineering detail

    The API reads the frame index, stores tiles in R2, and redirects the map to the CDN copy.

  • Push

    Expo push

    Notifications on the phone: a scheduled stream is about to start.

    Why. One delivery service for iOS and Android.

    Engineering detail

    The app registers its Expo push token with the API; the daily reminder job sends through Expo’s push service. Tapping the notification opens the watch screen.

  • Email

    Postmark

    Sign-in codes, magic links and the “you’re approved” email.

    Why. Transactional email that arrives quickly and is audited.

    Engineering detail

    Sent from the API through the Nesting email module, with an audit record per message.

  • Desk alerts

    Slack

    Tells the desk when someone goes live or signs up, with approve and reject buttons right in the message.

    Why. The desk already lives in Slack. Approving a new chaser should not need a trip to another tool.

    Engineering detail

    Jobs post the alerts; button clicks come back to the API’s Slack webhook, which verifies Slack’s signature before acting.

03 · Journeys

What happens when you press the button.

The same parts, seen from one action at a time. In the map above, pick a journey to see its path light up.

Go live

From tapping Go live to viewers seeing the first frames. The video goes from the device straight to Cloudflare; the API only decides, records and tells people.

  1. 01Create the streamThe app asks the API for a stream. The API creates a Cloudflare live input, switched off so it costs nothing while idle, and saves the row.
  2. 02Tap Go liveThe API switches the input on and hands back where to send video: RTMPS for the glasses, the phone and encoders, WHIP for a browser.
  3. 03The picture leaves the deviceThe glasses pass the picture to the phone, which publishes it. A browser or an encoder publishes the same way. None of it passes through our servers.
  4. 04Cloudflare says it’s connectedA signed webhook tells the API the first frames arrived, and the stream is marked live. A later disconnect is only recorded: on a cell link, a drop is usually a reconnect.
  5. 05Everyone hearsThe change goes to every copy of the API through Redis, down every open watch-list socket, out to the landing map at the edge, and to the desk in Slack.

Watch

Finding a broadcast, getting permission to play it, and drawing the credit, place and time over it without adding delay.

  1. 01Find what’s liveThe watch list arrives over a WebSocket: a snapshot, then each change. Every change is re-checked against what that viewer is allowed to see.
  2. 02Get a playback tokenThe API checks access and signs a playback token that lasts 15 minutes. A share link works the same way, with the link itself as the credential.
  3. 03PlayThe player pulls low-latency HLS from Cloudflare’s CDN. Viewers never use WebRTC, so a crowd is just CDN traffic.
  4. 04Know when each frame was shotPlaylists pass through the playlist proxy, which stamps each segment with its capture time, so the clock on the picture is right through pause and seek.
  5. 05Draw the readoutThe player fetches the overlay data (name, role, place, time zone, never coordinates) and draws the template in a sandboxed frame over the picture.
  6. 06The map around itThe map draws Protomaps tiles; radar comes from R2 after the API fetched it once; weather comes through the API.

Tags and the route

Rolling out

While live, the phone records where it is every five seconds, and the broadcaster tags weather events with a tap. On playback both line up with the picture: the tag for five seconds, the map for the whole broadcast.

  1. 01Note where, every five secondsThe phone takes a fix every five seconds and sends what it has every five seconds. Each fix says how long ago it was taken, and the API stamps it with its own clock: phone clocks drift, and the player’s clock comes from Cloudflare. A resent batch is ignored.
  2. 02Tag the momentThe broadcaster taps an event from the desk’s list. The tag is pinned to the tap, even if it reaches the API after a retry, and a repeated tap cannot make two. The desk can add, move or remove tags in the CMS, live or after.
  3. 03Keep it with the broadcastFixes and tags are kept against the stream. A stream is reused for many broadcasts, so every read is cut to one: the broadcast on the air, or the span of one recording.
  4. 04Line it up with the pictureThe player compares each tag and fix with the time the frame on screen was shot, the same clock the readout uses. A tag shows for five seconds, then fades; the map’s dot follows the route, and jumps on a seek. Live players ask every five seconds for what is new.
  5. 05Exact for the owner, rounded for everyone elseThe owner and the desk get every fix. Anyone else gets the route rounded to the public pin’s grid, about a kilometer: one point per square, with times rounded too, and no heading or speed. A phone has to be well past a square’s edge before the route moves on, so GPS jitter cannot pin it to a line. No public location, no route.

Stop → replay

Every broadcast becomes a replay without anyone pressing anything, and a dropped connection never ends one by accident.

  1. 01Tap StopOnly the owner’s Stop ends a broadcast. The API marks it ended and queues the replay, but leaves the Cloudflare input on: switching it off early cut Cloudflare’s packaging short and broke the recording.
  2. 02Wait for the recordingA background job checks Cloudflare until the recording is ready, retrying as long as it takes.
  3. 03Save the replayThe job saves the replay as a video, copies its poster to R2 so public links get a stable image, then switches the input off.
  4. 04Sweep upEvery 30 minutes a reconciler finds streams that were left half-way, for example after a crash, and finishes them.
  5. 05Watch it laterThe replay plays from Cloudflare with a signed token, in place on the watch page, the map or the desk.

Upload

Shot without signal, sent later. The bytes go straight to Cloudflare in chunks that survive a dropped connection.

  1. 01Pick a videoThe app asks the API to begin an upload. The API checks the limits (30 minutes, 2 GB) and gets a one-time upload address from Cloudflare.
  2. 02Send the bytesThe device sends the file straight to Cloudflare in 5 MiB resumable chunks while the owner fills in the form. A dropped connection costs one chunk, not the file.
  3. 03Cloudflare finishesA signed webhook says the video is ready, and the API links it to its record.
  4. 04PublishIt stays private until the owner publishes it. New tags wait for the desk to approve them in the CMS.

Landing map

The public home page shows who is live and what the desk is featuring, pushed from the edge rather than from the API.

  1. 01Something changesA public stream goes live or ends, or an editor changes the featured place in the CMS.
  2. 02Publish to the edgeThe API publishes the whole map state to the Durable Object, which drops anything older than what it already holds.
  3. 03Push to every visitorThe object pushes the state to every open home page over hibernating WebSockets, so idle visitors cost nothing.
  4. 04Works without itThe page renders from the API first, so it is complete even if the socket never connects.

Build a package

In progress

One owner’s story of a storm day: streams, replays, uploads, photos and posts, in their order, shown to each reader only as far as each piece is public.

  1. 01Start itThe owner names the package on the phone. It starts private, and the API records it with an order version.
  2. 02Add photosThe phone re-encodes each photo to JPEG, which drops the location data the camera saved, and sends it straight to R2 on a 15-minute upload address. The API then checks the object is really an image of the size it was told.
  3. 03Write it upThe description and posts are rich-text documents. The API keeps only the parts it allows (headings, lists, links, photos), so a stored post can never carry markup or scripts.
  4. 04Check the picturesAsking for public queues a scan of each photo, the same model and thresholds as video. From an unverified account the words wait for a person in the CMS.
  5. 05Read itEach reader gets only what they could see on its own: a private clip in a public package stays private. Photos come as short-lived signed addresses, and a shared link opens with no account.

Burned-in copy

In progress

For video that leaves our players: a copy with the credit, place and time in the pixels. The original stays clean.

  1. 01Order itAn editor asks for a burned-in copy of a video in the CMS.
  2. 02Freeze what it saysThe API saves what the overlay will show (name, role, credit, place, time zone) with the render, so it can be redone with the same words later, and gets a one-time upload address.
  3. 03Hand it to the compositorA signed request starts a session: a sealed browser draws the template, and a separate worker does the video.
  4. 04Burn and uploadThe worker reads the clean video, lays the overlay on one frame a second, encodes, and uploads the result to Cloudflare as its own video.
  5. 05Report progressThe CMS polls; the API asks the compositor and Cloudflare and reports download, overlay, encode and upload.

04 · Why it’s built this way

The decisions that shape it.

Most of the system follows from a few choices. Each one has a reason, and most of the reasons were learned the hard way.

  • Video never touches our servers.

    Cameras publish straight to Cloudflare, and viewers play straight from its CDN. Our API decides who may do what and keeps the records; it never carries a frame. That is what lets a modest server handle a severe-weather night.

  • One place decides who sees what.

    Every playback token is minted by the API after an access check and expires in 15 minutes, so a copied playback address soon stops working. Share links can be revoked because the link is itself a credential the API can stop honoring.

    Engineering detail

    Live playlists are signed locally with RS256 because Cloudflare’s token endpoint does not cover live inputs. Watch-list updates are re-checked per socket on every change.

  • A disconnect is not a stop.

    Cell links drop. Only the broadcaster’s Stop ends a broadcast, and Cloudflare holds the recording open for 90 seconds, so a reconnect continues the same broadcast instead of starting a new one.

    Engineering detail

    The live input stays on after Stop until the replay is saved: switching it off early truncated Cloudflare’s packaging and errored the recording.

  • Uploads go direct, in pieces.

    A field upload goes from the phone to Cloudflare in 5 MiB resumable chunks, starting the moment a video is picked. A dropped connection costs a chunk, and closing the app does not lose the upload.

  • The web app guards the door.

    Browsers reach the API through the web app’s server, which forwards only the routes on a fixed list and keeps the sign-in token in a cookie that page scripts cannot read.

    Engineering detail

    Exact-segment allow-list, no patterns; public routes never forward credentials. Refresh is serialized across tabs because a reused refresh token revokes the whole session family.

  • Crowds are served from the edge.

    The busiest things, the landing map, playlists and radar, are answered by Cloudflare’s edge, so a spike in viewers is a spike in CDN traffic rather than load on the API.

  • Later work is retried, and runs once.

    Turning a broadcast into a replay, reminders and clean-up are background jobs with retries and a record of every step. A scheduled job runs once however many copies of the API are up.

  • A clean original, two ways to credit it.

    Our players draw the credit, place and time over the picture, which adds no delay. Anything that leaves our players gets a burned-in copy. Either way the original stays clean, so a better design can be applied to everything already recorded.

    Engineering detail

    The overlay template runs sandboxed: in an opaque-origin iframe in the browser, in a WebView on the phone, and in a sealed microVM with no network and no secrets in the compositor.

  • One clock for every moment.

    A tag, a location fix and a frame of video are all placed by time, and the time is never the phone’s. The phone says how long ago something happened and the server stamps it, and the player knows when each frame was shot. So a tag lands on the right frame however late the signal was.

    Engineering detail

    Stream rows are reused across broadcasts, so nothing is stored as an offset from a start: every record is a server timestamp, and every read is cut to one broadcast or one recording’s span.

  • A machine reads the pictures before strangers see them.

    An account we know goes public when it asks. Anyone else’s video or photo is read by an image model first, start to finish, for sexual content, nudity, hate and hate symbols. A clear result publishes it with no one at the desk; anything unsure waits for a person. A failed check never counts as clear.

    Engineering detail

    One scanner sits in front of the vendor (Hive’s image API), so the stream, upload and photo code never call it directly. Video frames come from Cloudflare’s signed thumbnails, never from the phone. Lists and playback read only the public flag, so a bug in a scan cannot leak a held item through a second flag. Every request and every desk decision carries an epoch, and a scan that finishes after the owner or the desk changed course writes nothing.

  • The API stays an ordinary server.

    We looked at moving the whole API to the edge and decided against it: a rewrite of the whole thing to save little. Instead, the specific hot paths move to the edge one at a time.

05 · What’s next

Being built now.

These are drawn dashed on the map. Some run in development today; none are in production yet.

  • Burned-in copies in production

    The compositor runs in development today. Next: production, burned-in downloads and shares by default, and live burn-in for partner feeds.

  • Frame clock for everyone

    The playlist proxy moves from development to production, along with replays and the mobile app drawing the readout.

  • Watch list at the edge

    The watch-list socket moves from the API into a Durable Object, the way the landing map already works, and the cross-replica Redis relay retires after a soak.

  • More cached at the edge

    Radar tiles cached by Cloudflare in front of the API, then public weather alerts, storms and places served from the edge cache.

  • Packages

    Bundles of streams, videos, photos and posts with a rich description, on Today and Watch in the app, on the web, and shared by link. Built and tested; not in production yet.

  • Templates as content

    Overlay templates delivered from R2 and managed in the CMS, instead of shipped inside the app builds.

See it working.

Watch what is live now, or read what it does for you.