Skip to main content
Deploy the generated-video app before starting this walkthrough. Use its application ID, such as your-account/your-video-app. Do not append /start-session or supply a URL.
The WMA browser API is experimental. This walkthrough uses @fal-ai/client@1.11.0-alpha.5, independently of the stable Python fal package. The JavaScript latest version does not necessarily include these APIs. Pin the version shown below.

Install the client and server proxy

Run this command in your web application:
Keep FAL_KEY in your server environment. Never put the key in browser code or a NEXT_PUBLIC_ environment variable. The browser sends authenticated requests through your server proxy. The proxy adds the fal credential. For a Next.js App Router application, create app/api/fal/proxy/route.ts:
The explicit URL patterns permit WMA signaling and the app ICE fallback. The default proxy patterns do not include wma.fal.run. This minimal route is suitable for a local development app. Before exposing it, require your application’s user authentication and authorization on the route. Limit access to the endpoints that your application needs, and apply appropriate rate limits. The proxy’s fal credential does not authenticate your application’s users. WMA signaling needs streaming responses. Keep response streaming enabled on your proxy and hosting platform. The Next.js Pages Router proxy does not support streaming responses. Use the App Router integration above.

Receive video and send controls

Add these elements to a page:
Run the following TypeScript in the browser after the elements exist. Use your application’s bundler to resolve the package imports. Replace the application ID with the ID from your deployment.
Select Connect, then Fast sunset. The app updates the video and acknowledges the command in the message area. Select Disconnect to release the session. In a component framework, also call close() when the component unmounts. fal.realtime.open() returns a handle immediately. Its ready promise resolves when the session is ready. Use onState for lifecycle changes, onData for raw message strings, and onMedia for incoming streams. Parse and validate onData messages before your application uses their values.

Choose the media direction

The client options must match the deployed app’s contract. These directions are from the browser’s perspective: receive: [] creates a session without incoming media tracks. Control messages can still travel in both directions. For audio output, request receive: ["audio"] and attach the received stream to an audio element.

Send camera video

Call getUserMedia() from an explicit user action, such as a Connect button. Camera capture requires HTTPS or localhost and browser permission. This function connects to the camera-effects example. It returns a cleanup function for your Disconnect button or component unmount handler.
The client does not own your camera stream. Stop its tracks when you finish, including after connection failures. Disable repeated Connect actions while opening. Retain the returned cleanup function and call it when leaving the page. For a UI that cancels during opening, retain the session handle immediately and call close() before ready resolves. For motion measurements, change receive to [] and use the motion app ID. Read its JSON measurements through onData. That app does not return video.

Verify network and lifecycle behavior

The WMA extension requests ICE configuration through the authenticated bridge. To test a TURN-only path, add iceTransportPolicy: "relay" to the options passed to fal.realtime.open(). This test requires working TURN provisioning. Do not substitute a public STUN server for TURN. Use the returned session’s connection information when diagnosing network failures:
Disconnect before creating a replacement session. Keep the endpoint ID and each app’s media contract consistent. For server cleanup and billing checks, see the WMA deployment guide.