drfreeze

drfreeze

I built a WebTransport transport for Phoenix sockets and LiveView and put it on GitHub with a demo and a spec: GitHub - jfreeze/phoenix_web_transport: WebTransport (HTTP/3, QUIC) transport for Phoenix LiveView: one QUIC stream per component, shortest-message-first. Working prototype. · GitHub

The idea is small. A LiveView pushes every diff down one WebSocket, which is one ordered TCP stream, so a 50-byte counter update queued behind a 300 KB table diff waits for the whole table, and one lost packet stalls everything behind it. LiveView’s diff format already splits by component, so this library puts each component’s deltas on its own QUIC stream and sets the stream priority from the frame size, smallest first. The browser side is a WebSocket-shaped class handed to LiveSocket through the transport option. Nothing in Phoenix, LiveView or phoenix.js is patched; the server side is a Phoenix.Socket.Transport plus a serializer.

The QUIC listener runs beside your existing Bandit endpoint. It uses cowboy’s experimental HTTP/3 and WebTransport layer on top of emqx’s quicer (msquic), because nothing else on the BEAM speaks QUIC yet. Bandit stays in charge of HTTP and the WebSocket fallback.

Status: it works end to end in Chrome against a local server, with the two-component demo page showing lane 0 for the join, lane 1 carrying kilobytes for the small component and lane 2 carrying megabytes for the table. On unthrottled loopback both transports measure the same, which is expected since the browser’s DOM patch dominates there. I have not measured it over a real network. A cowboy PR adding a set_stream_priority WebTransport command is up at Add a set_stream_priority WebTransport command by jfreeze · Pull Request #1725 · ninenines/cowboy · GitHub, and cowboy’s own suite passes with it.

Where I think this pays off is LiveView on mobile: bursty loss, long round trips, and QUIC’s per-stream recovery and one-round-trip handshake on top of the split. It needs a direct UDP path with a public cert, so it will not work behind Cloudflare Tunnel, which is how I host, so I cannot run the test that matters myself.

Two asks. If you serve LiveView to phones and have a host you can open a UDP port on, I would like a phone-on-cellular measurement with the demo page. And if anyone is working on QUIC for Thousand Island or Bandit, I would rather build the listener on that than on cowboy.

Known gaps are listed in the README: automatic fallback to WebSocket on handshake failure, auth on connect (WebTransport sends no cookies), and a per-diff sequence number so events pushed alongside a component delta fire after it lands.

Showing Posts 1 to 9

mat-hek

mat-hek

Membrane Core Team

How about GitHub - benoitc/erlang_quic: Pure Erlang QUIC implementation (RFC 9000) · GitHub?

drfreeze

drfreeze OP

Duh. I missed that, thank you.

erlang_quic turned out to have everything the listener needs.
I built a second session layer on it, about 300 lines and no native code. Chrome connects end to end with the same per-component lanes as the cowboy version.
The README now also credits bugnano’s wtransport-elixir, which I should have listed in the post.

erlang_quic is now the default backend with cowboy and quicer as optional deps.

Do you have experience running erlang_quic anywhere to see how it holds up?

P.S. I should note that the new version uses Bandit and does not require cowboy.

gtcode

gtcode

I built a real time media system in Elixir with LiveView this summer. I really wanted to use QUIC since it would be superior to a traditional sockets/TCP approach in terms of ordering restriction and cancellations for that use case.

Chose not to due to maturity of the libs vs other competing languages. But, building the frontend in C++, Rust or Go would not have been as joyous.

Glad to see people working on things like this. Elixir needs more attention in areas like QUIC.

mat-hek

mat-hek

Membrane Core Team

We built ex_moq, Elixir bindings to Media Over QUIC, and a Membrane plugin if you’re interested :wink:

Unfortunately no

gtcode

gtcode

Interesting – in my generative-media data plane, committed media and replacement candidates currently share an ordered WebSocket. I extracted the splice/adoption semantics above the transport (unfinished private project kicked off from the MVP post-mortem, of course). QUIC/MoQ/ex_moq could give those flows independent delivery. WebSockets could remain as fallback.

drfreeze

drfreeze OP

Thanks, and no worries on erlang_quic.

ex_moq doesn’t replace the server side here, but it changed my hosting statement. Cloudflare runs MoQ relays with WebTransport to the browser and an outbound QUIC connection from the origin. That is the first way I’ve found to get QUIC streams to a LiveView through Cloudflare without a public UDP port.

Have you pushed non-media tracks through the Cloudflare relay with ex_moq, and how did latency compare to a direct connection?

mat-hek

mat-hek

Membrane Core Team

We’re mostly sending media and through our own relays :wink:

byhemechi

byhemechi

You should be careful with erlang_quic. There’s no buffering on sends, if you send a large message on a high latency connection there is a very high chance you’ll pass the bytes in flight limit and crash. This probably won’t happen in most LiveView scenarios but some things (base64 encoded images are the first thing that come to mind) will break.

drfreeze

drfreeze OP

You were right, thank you. I reproduced it with a 40 MB update.

erlang_quic refuses to send once 16 MB is waiting.

It can also send more than the browser has said it will accept. Chrome closed the connection for that in one run out of four.

My code now sends in small pieces and waits when erlang_quic is backed up. 125 MB per run gets through, and small updates still arrive while a big one is in flight.

That seems to work around it for now.

— All posts loaded —

Where Next? Top

Trending in Discussions Top

cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
achempion
I’ve been using Emacs as my main code editor for more than a two years. It’s a custom build version although I’ve tried doom emacs and sp...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews