Quick start
If you want to learn kuilt quickly, start with InMemoryLoom. It gives you two peers exchanging real Seam frames with no sockets, no radios, and almost no setup. It is ideal for tests and for building app behavior before choosing a real transport.
Host a session and broadcast a frame
/**
* A broadcast from one peer is received by all other peers.
*
* Alias for the `broadcast from A causes B to receive the frame` test.
*/
@Suppress("unused")
internal fun sampleBroadcastReceived() = runTest {
val factory = InMemoryLoom()
val a = factory.host(Pattern("Alice"))
val b = factory.join(InMemoryTag("Bob"))
val receivedByB = async { b.incoming.first() }
a.broadcast(byteArrayOf(1, 2, 3))
val frame = receivedByB.await()
assertEquals(Swatch(byteArrayOf(1, 2, 3), sender = a.selfId, sequence = 1L), frame)
}
Three things to notice:
InMemoryLoom uses one shared instance for both host and join. Both seams share one in-memory mesh. Radio-based fabrics (Nearby, Multipeer) work the same way.
Collect incoming once. All frames for a peer arrive on one Flow<Swatch>. Collect it in one coroutine; if multiple parts of your app need the frames, wrap it with shareIn.
sender and sequence are set when a frame is received. The sender leaves sender as null and sequence as zero; the receiving Seam fills them in.
Membership: peers join and leave
/**
* After host and join, both peers appear in each other's [Seam.peers] set.
*
* Alias for the `join after open causes both peers to appear in each other's peer set` test.
*/
@Suppress("unused")
internal fun sampleJoinPeerSet() = runTest {
val factory = InMemoryLoom()
val host = factory.host(Pattern("Alice"))
val joiner = factory.join(InMemoryTag("Bob"))
assertEquals(setOf(host.selfId, joiner.selfId), host.peers.value)
assertEquals(setOf(host.selfId, joiner.selfId), joiner.peers.value)
}
peers is a StateFlow<Set<PeerId>>. Both host and joiner see the same set. When a peer closes, it is removed from every other peer's peers set atomically.
The interaction pattern (every fabric)
// 1. Get a Seam — host or join, depending on your role.
val seam: Seam = loom.host(Pattern(displayName = "alice", maxPeers = 4))
// 2. Collect incoming frames once. Fan out with shareIn if needed.
scope.launch {
seam.incoming.collect { swatch ->
println("from ${swatch.sender}: ${swatch.decodeToString()}")
}
}
// 3. Send. broadcast() reaches all peers; sendTo() targets one.
seam.broadcast("hello everyone".encodeToByteArray())
seam.sendTo(somePeerId, "just for you".encodeToByteArray())
// 4. Watch membership.
scope.launch { seam.peers.collect { current -> render(current) } }
// 5. Close when done. Idempotent.
seam.close()
This pattern is the same for every fabric: swap the Loom, keep the app code.
Last modified: 22 June 2026