Choose handlers or one update function
Register one handler for each topic pattern. beryl/channel routes each event to the correct channel. Each channel keeps private, typed state. You can also route all events in one typed update function.
Type-safe real-time channels and presence for Gleam, targeting the Erlang (BEAM) runtime.

import beryl/channelimport gleam/json
// One handler per topic pattern, with private typed state.pub fn room() -> channel.Handler { channel.handler("room:*", fn(ctx) { channel.accept(0) |> channel.on_message(fn(count, msg: channel.Message) { case msg.event { "new_msg" -> channel.next(count + 1, [ channel.broadcast("new_msg", json.int(count + 1)), ]) _ -> channel.stay(count) } }) |> channel.with_reply(json.string(ctx.topic)) })}Install beryl packages from GitHub. Add beryl and one WebSocket transport as Git dependencies. The beryl package includes the channel layer.
gleam deps download
Write a channel handler. Connect the WebSocket transport and a Phoenix JS client. If you want to control routing, compare the two APIs.
Open the Quick Start →Use a complete example. Then read the channel guide or the API reference.
View examples →Register one handler for each topic pattern. beryl/channel routes each event to the correct channel. Each channel keeps private, typed state. You can also route all events in one typed update function.
Track connected users across Erlang nodes. A conflict-free replicated data type (CRDT) resolves joins and leaves that happen at the same time.
Presence guide →Send events between connected Erlang nodes through pg process groups. You do not need a separate message broker.
Use beryl's Phoenix codec with Phoenix client libraries. Mist and Ewe connect beryl to a WebSocket server. Phoenix users can compare the concepts.
WebSocket guide →