Drop: how we send files of any size straight between browsers, torrent-style, over WebRTC
Drop is a free tool at streaming-cdn.ewin888.com/drop. You open a room, scan its QR code on another device, and send files or chat in real time. It needs no sign-up and no app, and there is no size limit on a direct connection. We built it with the same browser real-time stack we run for live video, and this article explains how it works. Every number below is a constant in the shipping code.
What it does
- A room holds up to 12 people. Each picks a nickname; there are no accounts.
- Files of any type and size, as many as you like. On a direct connection nothing caps the size.
- Everyone who downloads a file also shares it, so a file spreads faster as more people have it. It stays available after the original sender leaves.
- Unfinished downloads are kept on the device's disk, so leaving the room and coming back continues where it stopped.
- Chats and files are not stored on any server. When everyone has left, the room is empty.
1. The server only introduces the browsers
Two browsers cannot open a WebRTC connection until they have swapped an SDP offer, an answer and their ICE candidates. Drop's server does exactly that and nothing else: each room is a small WebSocket signaling room that relays those messages and keeps the member list. Chat messages and file bytes never pass through it. A signal is capped at 64 KiB and a socket at 400 messages per 10 seconds, so the room cannot be used as a message bus.
Each pair of members opens one RTCPeerConnection with two negotiated data channels: channel 0 carries chat and control messages as JSON, and channel 1 carries file bytes. A newcomer gets the room's chat history (up to 300 messages) from a member who is already there, not from the server.
2. Direct first, relay only when it must
The page first asks only for discovery (STUN) servers, which tell each browser its public address. That is enough for most pairs: two devices on the same Wi-Fi, or on most home and mobile networks, connect directly. If no direct path comes up within 6 seconds, the page asks for relay (TURN) credentials and retries through the relay.
| Direct | Relayed | |
|---|---|---|
| Path | Device to device | Through an encrypted relay |
| Who can read it | Only the two browsers (DTLS) | Only the two browsers; the relay forwards encrypted packets it cannot decrypt |
| File size | No limit | 200 MB per file |
| Allowance | None | 8 relay credentials per network address per day, each valid one hour |
Relayed traffic is the only part of Drop that costs us bandwidth, which is why it has an allowance. The interface shows which link each member is on, and tells you to move both devices onto the same Wi-Fi if you need to send a large file over a relayed pair.
3. Sending fast without running out of memory
RTCDataChannel.send() never blocks: it queues. Push a 4 GB file into it in a loop and the tab runs out of memory. Drop sends 64 KiB chunks and stops when the channel's bufferedAmount passes 4 MB, then resumes when it drains below 1 MB:
const CHUNK = 64 * 1024;
const HIGH = 4 * 1024 * 1024;
const LOW = 1024 * 1024;
channel.bufferedAmountLowThreshold = LOW;
async function sendBlob(channel, blob) {
for (let offset = 0; offset < blob.size; offset += CHUNK) {
if (channel.bufferedAmount > HIGH) {
await new Promise((resolve) => channel.addEventListener("bufferedamountlow", resolve, { once: true }));
}
channel.send(await blob.slice(offset, offset + CHUNK).arrayBuffer());
}
}
blob.slice() reads from disk only the bytes being sent, so the sender never loads the whole file either.
4. Pieces, hashes and the swarm
Sending a whole file from one person to everyone else would put the full upload on the sender's connection. So Drop shares files the way BitTorrent does. Before sharing, the page cuts the file into pieces of at least 1 MiB, using no more than 2,048 pieces per file, and hashes each one with SHA-256 in the browser. The file's announcement carries a root: SHA-256 over all the piece hashes, in order.
async function describe(file, pieceSize) {
const hashes = [];
for (let offset = 0; offset < file.size; offset += pieceSize) {
const piece = await file.slice(offset, offset + pieceSize).arrayBuffer();
hashes.push(new Uint8Array(await crypto.subtle.digest("SHA-256", piece)));
}
const joined = new Uint8Array(hashes.length * 32);
hashes.forEach((hash, i) => joined.set(hash, i * 32));
const root = new Uint8Array(await crypto.subtle.digest("SHA-256", joined));
return { hashes, root };
}
- Any member can hand a downloader the list of piece hashes (the manifest). The downloader keeps it only if it matches the announced root.
- Each received piece is hashed and compared with the manifest. A bad piece is thrown away, so a broken or dishonest peer cannot corrupt the file.
- Members announce which pieces they hold. A downloader asks for the rarest missing pieces first, from several members at once: up to 3 requests per member and 16 in total, choosing the least busy holders. A piece that does not arrive within 30 seconds is asked from someone else.
- As soon as a member holds a piece, it serves that piece to others. So the more people have a file, the faster it goes, and the file stays available as long as the members online between them hold every piece.
5. Downloads live on disk, not in memory
A phone cannot hold a multi-gigabyte download in memory. Drop writes pieces into the browser's origin private file system (OPFS) from a dedicated worker, using synchronous access handles, the one OPFS write path that every current engine supports. Alongside the data it stores a small record of which pieces it holds, one bit per piece.
- Leave the room, close the tab, or lose the connection: when you come back, the download continues from the pieces already on disk.
- A download you have not saved yet is kept for 30 days. Once you save it to your own folder, the temporary copy is removed on your next visit.
- Where OPFS is unavailable, as in some private windows, pieces are kept in memory instead.
6. Keeping a phone transferring when the screen locks
Mobile browsers suspend a page when the screen locks, which would stop every transfer. Drop does two things. While files are moving, it holds a Screen Wake Lock so the idle timer does not lock the phone. A background mode keeps the room running even when the screen is locked, on iPhone too. Background mode works by playing a looping silent audio track, because a page that is playing audio is allowed to keep running. The trade-off is that music in other apps pauses while background mode is on, so it can be switched off.
What Drop is not
- Not storage: once the last member holding a file leaves, nobody can download it until someone who has it comes back.
- Not a server-side copy: nothing is uploaded to us, so there is nothing for us to scan, keep or lose.
- Not unlimited over a relay: a relayed pair is limited to 200 MB per file and a daily allowance; the same Wi-Fi removes both limits.
Try it
Open streaming-cdn.ewin888.com/drop, create a room, and scan the code with your phone. It works in current Chrome, Edge, Firefox and Safari. Drop is free, and the page carries a small note about our main product, which is low-latency live streaming. The same WebRTC stack that runs Drop delivers live video to browsers worldwide. If you need that, start at live streaming.