Skip to main content
Transform live camera video with AI on mobile devices. This walkthrough covers the architecture and key integration points of an open-source Expo app that connects to Decart’s realtime models — including Lucy 2.1 for character transformation.
This example uses Expo with LiveKit’s React Native WebRTC stack. React Native realtime requires the LiveKit packages and an early registerGlobals() call — plain react-native-webrtc is not supported. See LiveKit setup for React Native below.
Source code: github.com/DecartAI/decart-example-expo-realtime

What you’ll build

Model switching

Switch between Lucy Restyle Live, Lucy editing, and — after a quick upgrade — Lucy 2.1 for character transformation.

Style presets

Swipe through curated looks: anime characters, tuxedos, superhero costumes, and more. Each preset is a text prompt sent to the model.

Camera controls

Toggle front/back camera, switch view modes (fullscreen, picture-in-picture, split), and see your original and transformed feeds side by side.

Prerequisites

  • Node.js 18+
  • Expo (npx expo — no global install needed)
  • A Decart API key from platform.decart.ai
  • A physical iOS or Android device (WebRTC requires real camera hardware)

Project setup

1

Clone the repository

2

Install dependencies

3

Add your API key

Create a .env file in the project root:
4

Run on a device

WebRTC requires a physical device. The iOS Simulator and Android Emulator do not support camera streaming.

LiveKit setup for React Native

React Native realtime runs on LiveKit’s React Native packages. Plain react-native-webrtc is not supported. The cloned example already wires this up — here’s what makes it work. 1. Install the LiveKit packages (already in the example’s package.json):
2. Register LiveKit’s native globals before the app starts. The SDK reads the WebRTC globals that LiveKit installs, so registerGlobals() must run before your router or root component is imported:
3. Add the Expo config plugins. Install them and register them in app.json:
Then run npx expo prebuild and rebuild the native app. LiveKit does not run in Expo Go — you need a development build on a physical device.
Using bare React Native (CLI) instead of Expo? The realtime code is identical, but you need two Metro/Babel config changes (Expo applies them for you). See React Native CLI (bare projects).

Architecture overview

The app uses Expo Router with three screens: Key directories:

Core integration: the WebRTC hook

The useWebRTC hook handles the entire Decart SDK lifecycle — creating a client, capturing the camera, connecting to a realtime model, and managing cleanup.
Three things to note:
  1. Model constraints drive camera setup. Each model defines its own fps, width, and height. Resolve the FPS with resolveFpsNumber(model.fps) (it can be a descriptor object rather than a plain number), and getMediaStream requests exactly those dimensions from LiveKit’s mediaDevices.
  2. preferredVideoCodec: "vp8" selects the codec. React Native works best with VP8. The SDK negotiates it for you — there’s no manual SDP rewriting (the old customizeOffer / forceCodecInSDP approach is gone).
  3. No MediaStream casts. connect<MediaStream>(...) takes LiveKit’s React Native MediaStream type as a generic, so you pass the captured stream straight through — no as unknown as bridging.

Switching models

Each model offers different creative capabilities: Switch models by creating a new ModelDefinition and reconnecting:
Use Lucy 2.1 as the default for new mobile integrations. It supports both character reference and text-only editing.

Applying style presets

The app includes curated prompt presets for each model. When a user selects a preset, the prompt is sent to the active model:
Example prompts from the preset list:
For Lucy 2.1, use set() instead of setPrompt() to include a reference image:

Handling app lifecycle

Mobile apps frequently move between foreground and background. The example handles this by disconnecting when backgrounded and reconnecting when foregrounded:
Always disconnect when backgrounded. WebRTC connections consume battery and network resources even when the app isn’t visible.

Camera controls

Mirror the front camera. The SDK’s mirror option is browser-only and is rejected on React Native with UNSUPPORTED_PLATFORM_FEATURE. Mirror the local preview at the view layer instead — pass mirror to LiveKit’s <RTCView>:
Switch facing mode. The camera stream needs to be recaptured when toggling between front and back cameras, since each has different hardware capabilities.

Adding Lucy 2.1 support

Adding Lucy 2.1 requires two small changes: updating the model selector and using the set() method for character reference images.
1. Add Lucy 2.1 to the model selector:
2. Use set() for character reference images: Lucy 2.1’s key feature is character transformation via reference images. Replace setPrompt() with set() when a reference image is available:

Next steps

Lucy 2.1 guide

Character transformation, reference images, and the set() API in detail.

JavaScript SDK

Full SDK reference for connection management, events, and client-side auth.

All realtime models

Compare Lucy 2.1 and Lucy Restyle Live side by side.

Source code

Clone the full example app and start building.