Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
23693ac
Add Smelter Cloud documentation section
Gawor270 Jul 23, 2026
d240d02
Publish the generated Smelter Cloud OpenAPI spec and align docs
Gawor270 Jul 24, 2026
abd8188
Make the smelter-cloud spec source a submodule like the other APIs
Gawor270 Jul 24, 2026
6fd1611
Address review comments
Gawor270 Jul 24, 2026
6f781f3
Remove em dashes from the Smelter Cloud docs
Gawor270 Jul 24, 2026
123ed00
Move the Smelter Cloud API reference under the API Reference tab
Gawor270 Jul 24, 2026
31d5153
Add Smelter Cloud REST API to the API Reference navbar dropdown
Gawor270 Jul 24, 2026
7df745d
Unify the API Reference entry point
Gawor270 Jul 27, 2026
78fb511
Give the API Reference tab its own sidebar
Gawor270 Jul 27, 2026
58e4c29
Integrate compositions into the core docs structure
Gawor270 Jul 27, 2026
cb9d0c6
Merge branch 'main' into fce-2924-2925-smelter-cloud-docs
Gawor270 Jul 27, 2026
4f3f938
Rename the composition submodule key to match its path
Gawor270 Jul 30, 2026
5331814
Update the composition spec to the merged template-workers tip
Gawor270 Jul 30, 2026
3bdab1c
Drop the composition spec presentation overlay
Gawor270 Jul 30, 2026
1ffbfcb
Correct composition lifecycle and forwarding details
Gawor270 Jul 30, 2026
ed734ae
Fix composition docs against the live API and track foundry main
Gawor270 Aug 6, 2026
86e0307
Merge branch 'main' into fce-2924-2925-smelter-cloud-docs
Gawor270 Aug 6, 2026
ad2af21
Show the composed stream's destination in the tutorial diagram
Gawor270 Aug 6, 2026
adc70b4
Move scene updates into the tutorial and how-to
Gawor270 Aug 6, 2026
f2035e3
Point the Smelter links at the right reference
Gawor270 Aug 6, 2026
4a1b8da
make main tutorial easier
Gawor270 Aug 6, 2026
b8e9492
improve livestream input introduction
Gawor270 Aug 6, 2026
57915c8
template tutorial make better
Gawor270 Aug 6, 2026
30becb5
make fishjam room compose tutorial better
Gawor270 Aug 6, 2026
eb3e56d
add warnings about cleaning up
Gawor270 Aug 6, 2026
652abc9
Add a markdownlint config
Gawor270 Aug 7, 2026
aa42871
Drop globs from the markdownlint config
Gawor270 Aug 7, 2026
87a996f
reduce reference to minimum
Gawor270 Aug 7, 2026
99d830f
update smelter version to 0.3.0
Gawor270 Aug 7, 2026
c7e0c37
improve text
Gawor270 Aug 7, 2026
f811b49
Document the output resolution limits
Gawor270 Aug 7, 2026
89dee2e
Refresh the composition spec from the pending API changes
Gawor270 Aug 11, 2026
30e2567
Fold the template pitch into the tutorial's next steps
Gawor270 Aug 11, 2026
777ae62
Name the two API surfaces the way the reference does
Gawor270 Aug 11, 2026
4c12a54
Stop describing compositions in terms of the hardware under them
Gawor270 Aug 11, 2026
de3cb17
Say plainly that a composition cannot be watched directly
Gawor270 Aug 11, 2026
a1fd3df
Name the destinations instead of calling them somewhere else
Gawor270 Aug 11, 2026
4d888f3
remove crappy text
Gawor270 Aug 11, 2026
ff20df1
Fix the gaps found reviewing the composition docs
Gawor270 Aug 11, 2026
58dce02
simple text corrects
Gawor270 Aug 13, 2026
0c7d341
Call the corner layout the same thing in all three places
Gawor270 Aug 13, 2026
daa9c82
Show the composed frame instead of drawing it in ASCII
Gawor270 Aug 13, 2026
2666efe
Resolve the video path against the site base URL
Gawor270 Aug 13, 2026
84f6d54
Show only the starting layout in the tutorial clip
Gawor270 Aug 13, 2026
2f8fdc0
Create the tutorial livestream through the Sandbox API
Gawor270 Aug 13, 2026
d69e4b9
Revert "Create the tutorial livestream through the Sandbox API"
Gawor270 Aug 13, 2026
6cde3f7
Trim the sentence describing what you should see
Gawor270 Aug 13, 2026
0c711d2
Create the tutorial livestream through the Sandbox API, unlisted
Gawor270 Aug 13, 2026
5683558
Drop vdo.ninja and a playback claim the tutorial no longer supports
Gawor270 Aug 13, 2026
0dcb06d
Call the token the same thing in the prose and the placeholder
Gawor270 Aug 13, 2026
7aba97c
Point the events how-to at deploying and at getting a livestream
Gawor270 Aug 13, 2026
331ddda
Define every variable the composition how-tos use
Gawor270 Aug 13, 2026
d0dbb5e
Set up the composition and livestream right before deploying
Gawor270 Aug 13, 2026
3277582
Just say what to create, without the reasoning
Gawor270 Aug 13, 2026
32d68a3
Take the Sandbox API URL from the panel, not from the Fishjam ID
Gawor270 Aug 14, 2026
6430b52
Create the events composition after the template, not before
Gawor270 Aug 14, 2026
77cec77
Spell out the WHIP publish address correctly
Gawor270 Aug 14, 2026
0175555
Say what actually keeps a composition alive
Gawor270 Aug 14, 2026
a91550e
Refresh the composition spec from main and drop HLS input
Gawor270 Aug 14, 2026
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
[submodule "packages/python-server-sdk"]
path = packages/python-server-sdk
url = https://github.com/fishjam-cloud/python-server-sdk.git
[submodule "api/composition"]
path = api/composition
url = git@github.com:fishjam-cloud/foundry.git
49 changes: 49 additions & 0 deletions .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Rule reference: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
//
// Prettier is the formatter of record in this repo, so every rule it already
// governs is turned off here. Leaving them on produces thousands of warnings
// that contradict `yarn format`.
{
"config": {
"default": true,

// Prose is deliberately left unwrapped: `.prettierrc` sets no `proseWrap`,
// so Prettier defaults to "preserve" and never reflows paragraphs. Wrapping
// would also make one-word edits reflow whole paragraphs in diffs.
"MD013": false,

// MDX is JSX: <Tabs>, <TabItem>, and friends are not stray inline HTML.
"MD033": false,

// Docusaurus frontmatter carries the title, so files legitimately start
// without a heading and may repeat the title as an H1.
"MD025": false,
"MD041": false,

// House style: explanation pages open with an italic one-line subtitle.
"MD036": false,

// Repeated headings are fine under different parents ("Clean up",
// "Prerequisites"), but not as siblings.
"MD024": { "siblings_only": true },

// Whitespace, list, and emphasis style: all owned by Prettier.
"MD004": false,
"MD007": false,
"MD012": false,
"MD022": false,
"MD031": false,
"MD032": false,
"MD046": false,
"MD048": false,
"MD050": false,
},
"ignores": [
// Generated by TypeDoc from SDK sources.
"docs/api/**",
// Snapshots of released versions; fix issues in `docs/` instead.
"versioned_docs/**",
"node_modules/**",
"build/**",
],
}
1 change: 1 addition & 0 deletions api/composition
Submodule composition added at c6858e
6 changes: 6 additions & 0 deletions docs/_common/compositions/idle-cleanup.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:::warning[A composition bills for the whole time it exists]
With that guard off, cleanup waits for the outputs to fall silent too, so one that keeps
publishing is never cleaned up for you. Delete it as soon as you are finished, and do not
leave one running after a test. See
[Cost and lifecycle](../../explanation/compositions#cost-and-lifecycle).
:::
14 changes: 13 additions & 1 deletion docs/api/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type: reference

Describes APIs for direct interaction with Fishjam.

Fishjam publishes documentation for the Sandbox API and Fishjam Server APIs.
Fishjam publishes documentation for the Sandbox API, Fishjam Server APIs, and the Composition API.

## Sandbox API

Expand Down Expand Up @@ -51,3 +51,15 @@ the first message that must be sent is an `AuthRequest`,
with a valid Management Token.

Next, you can should subscribe to notifications by sending `SubscribeRequest` event with `SERVER_NOTIFICATION` event type.

## Compositions

[Compositions](../explanation/compositions) are managed through the Composition API. All requests go to `https://rtc.fishjam.io`.

### REST API

[Composition REST API Reference](/api/compositions)

Calls authenticate with your Management Token, from the [**Fishjam developer panel**](https://fishjam.io/app).

Request bodies are snake_case and closed: a field the endpoint does not define is rejected with `422` rather than ignored, so a misspelling fails loudly instead of silently doing nothing.
112 changes: 112 additions & 0 deletions docs/explanation/compositions.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
type: explanation
title: Compositions
sidebar_position: 4.5
description: Understand compositions, the Fishjam feature that mixes multiple live media streams into a single composed output in real time.
---

# Compositions

_Understanding real-time stream composition in Fishjam_

A **composition** mixes multiple live media streams into a single output stream in real time. You send inputs (WebRTC, RTMP, or MP4), describe how they should be laid out, and it produces one composed stream that it publishes to a destination of your choosing, all without running any rendering infrastructure yourself.

Compositions are built on [Smelter](https://smelter.dev), the source-available real-time video compositing engine by [Software Mansion](https://swmansion.com), and are a native part of Fishjam: they authenticate with the same Fishjam credentials and can compose the peers of a [Fishjam room](./rooms) directly.

## What you can build

- **Multi-party layouts**: arrange the cameras of a conference or livestream into grids, side-by-sides, or picture-in-picture.
- **Branded streams**: overlay logos, captions, lower-thirds, and backgrounds on top of live video.
- **Cross-protocol bridging**: take WebRTC inputs and republish the composed result over RTMP, for example broadcasting a conference to YouTube or Twitch, or the other way round.

## Core concepts

A **composition** is a single running compositing session. You register three kinds of things into it:

- **Inputs**: the live media sources being composed (WebRTC via WHIP/WHEP, RTMP, or MP4).
- **Outputs**: where the composed result is sent (WebRTC via WHIP, or RTMP). Each output carries a **scene** that describes the layout.
- **Renderers**: shared assets such as images and fonts you can place in a scene.

A composition produces video, it does not serve it. There is nowhere to point a player at a composition, so it always needs an output aimed at a destination your viewers can connect to instead: a [livestream](./livestreams) they watch over WHEP, or an RTMP service such as YouTube.

An output's scene can either be described directly in the API or rendered by a **template**: a React component, written with the layout components and the [`@fishjam-cloud/composition`](./../how-to/compositions/write-and-deploy-a-template) hooks, that updates the layout live as the room changes.

## Cost and lifecycle

A running composition holds a live rendering session for as long as it exists, and you are billed for that time whether or not anyone is watching.

Two defaults keep that in check. A composition auto-starts, and it cleans itself up after five minutes in which none of its inputs carry any media.

`cleanup_without_inputs: false` turns that guard off. It tightens the condition so cleanup needs both the inputs and the outputs to go quiet, which is what you want when inputs arrive late, such as a room whose peers have not joined yet, or when a composition legitimately has no inputs, such as an output that renders only text. The cost is that a composition whose output keeps publishing is no longer cleaned up for you, so anything created that way is yours to delete, and a forgotten one bills until you do.

Delete a composition as soon as you are done with it:

```bash
curl -X DELETE "$COMPOSITION_URL/api/composition/$COMPOSITION" \
-H "Authorization: Bearer $TOKEN"
```

## Scenes

Every video output carries a **scene**: a tree of components that describes how inputs, text, and images are arranged into the composed frame. Audio outputs carry an **audio scene** that describes which inputs are mixed together.

### The video scene tree

A video scene has a single `root` component. Each component has a `type` that determines how it lays out its children.

```json
{
"root": {
"type": "tiles",
"children": [
{ "type": "input_stream", "input_id": "camera_1" },
{ "type": "input_stream", "input_id": "camera_2" }
]
}
}
```

The available component types are:

| `type` | Purpose |
| -------------- | -------------------------------------------------------------------------------- |
| `input_stream` | Renders one registered input. Identified by `input_id`. |
| `view` | A container you position and style; the basic building block for custom layouts. |
| `tiles` | Automatically arranges its children into a grid. |
| `rescaler` | Fits a single child into a target area, preserving aspect ratio. |
| `text` | Renders a text string. |
| `image` | Renders a registered image. Identified by `image_id`. |

Components nest freely: a `tiles` of `rescaler`s wrapping `input_stream`s, a `view` with a `text` caption over an `input_stream`, and so on. The styling and full property set of each component come from Smelter itself; the [Smelter HTTP API reference](https://smelter.dev/http-api/overview) documents every component and its properties.

### The audio scene

An audio scene lists the inputs to mix and, optionally, their relative volume:

```json
{
"inputs": [
{ "input_id": "camera_1" },
{ "input_id": "camera_2", "volume": 0.5 }
]
}
```

`volume` defaults to `1.0`. Only the inputs you list are audible in the output.

### Setting a scene

You provide the initial scene when you register an output, under `video.initial` (a video scene) and `audio.initial` (an audio scene). See [Choose inputs and outputs](./../how-to/compositions/inputs-and-outputs) for the full output shape.

### Changing a scene over time

A scene is not fixed for the life of an output. You can replace it while the composition is running, immediately or at a chosen point on the composition timeline.

Either you push those updates yourself, or you hand the job to a **template**: a React component that receives the live room state and re-renders as the room changes. See [Choose inputs and outputs](./../how-to/compositions/inputs-and-outputs) to send an update, or [Write and deploy a template](./../how-to/compositions/write-and-deploy-a-template) to build one.

## Where to go next

- [Compositions tutorial](./../tutorials/compositions): create your first composition end to end.
- [Write and deploy a template](./../how-to/compositions/write-and-deploy-a-template): build a React layout with the composition SDK.
- [Compose a Fishjam room](./../how-to/compositions/compose-a-fishjam-room): turn a room's peers into one composed stream.
- [Composition API](./../api/reference#compositions): the full REST surface.
8 changes: 8 additions & 0 deletions docs/how-to/compositions/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Compositions",
"position": 5,
"link": {
"type": "generated-index",
"description": "Compose rooms and live streams into new outputs with templates, events, and multiple transports."
}
}
Loading
Loading