Device to device transfer¶
Two devices join the same network and a file moves straight from one to the other. No server sits in between, and this site never receives a single byte of it.
This page belongs to the lab. It exists to produce measurements rather than to be a tool you use every day.
Why measure this¶
Offline content on this site can only be updated over the network today. The QR code frame stream carries one to three KB per second, which rules out moving a few MB of offline content.
A direct connection inside one local network is far faster. The cost is that both devices first have to exchange a description of the connection, and that step normally relies on a server. Squeeze that description small enough and it fits in a QR code, which removes the server from the path entirely. This page measures how large that description is and how fast the transfer actually runs.
Running it on two devices¶
Put both devices on the same network. A hotspot you run yourself is the most predictable.
- Press "Start" in step 1 on both devices. Step 2 shows each device's own QR code, and the camera opens
- Point one device's camera at the other screen. The device that reads the code replies on its own, and its step 2 switches to a reply code
- Point the other camera back at that reply. The connection opens as soon as it reads
Nobody has to agree on who goes first; either device can scan first. The camera asks for permission the first time it opens, and scanning only works once it is allowed. Camera frames are decoded inside this page and are not sent anywhere.
Step 3 can switch to the front camera. Hold two phones screen to screen and both front cameras see the other code at once, so the exchange in both directions happens in a single gesture. Front cameras are usually fixed focus, and how close they need to be is one of the numbers this round measures.
When the camera is not an option, copy the whole text block under step 2 to the other device, paste it into step 3 and press apply. The result is the same.
Once connected, either side can send. The receiving side computes SHA-256 and compares it against the source, and a run only counts when they match.
Three devices or more¶
A third device does not need to scan every other device. One mutual scan with any device that is already connected is enough. Connected devices tell each other which devices they hold, and the device in the middle hands the connection descriptions across so the missing links open on their own. Step 4 marks each connection as either scanned directly or introduced by a named device.
Sending goes to every connected device at once, and step 5 lists timing and the hash check per device. Several devices share the same Wi-Fi channel, so each one runs slower than a one-to-one transfer, and how much slower is one of the numbers to measure.
Comparing throughput¶
Step 5 has three transfer settings: chunk size, channels per connection, and parallel connections. The defaults are 64 KB, 1 channel and 8 connections. Change one at a time and leave the others at their defaults. Both sides log the settings used for each run, which is what lets the results show which setting made a difference.
Parallel connections open several connections between the same pair of devices. In a 2026-09-25 test between an iPhone 15 Pro and Chrome on a Mac, the iPhone sent 100 MB at 4.8 MB per second over 1 connection and about 16.5 MB per second over 3, almost in proportion to the count. More channels inside one connection and different chunk sizes made no difference, so the limit applies per connection. A second round the same day on home Wi-Fi 6 (5 GHz) showed each connection from the iPhone holding at about 5.5 to 6 MB per second, about 33 MB per second over 6, with the round trip time staying under 20 ms. The Mac sent at 53 to 55 MB per second from 3 connections on, which is the ceiling of that network. Going further, the iPhone sent at about 37 MB per second over 8 connections and about 41 over 12, with each connection getting less. Twelve was only about a tenth faster than eight while its first transfer was the slowest of all, so the default stops at 8.
The first transfer right after extra connections open is clearly slower: with 8 connections, 100 MB from the Mac to the iPhone took 9 to 13 seconds instead of 2. Testing ruled out simply leaving the connections open for a while, since connections left idle for 7 to 14 seconds were just as slow the first time. Data has to flow over the new connections first. Warming each connection with 1 MB cut the first transfer to 3.5 seconds, still short of the 2 seconds of the second. The Warm-up option is there to find how much is enough, at 1, 4 or 16 MB per connection, and it runs in the background as soon as pairing finishes. Pick the same amount on both devices before pairing, then compare the first transfer. To try another amount, pick it and press "Drop extra connections". Every extra connection between the two devices closes, and the next send or warm-up opens a new set, with no reload and no new pairing. In the log, rttMs is the round trip time, and localNet and remoteNet give the class of each side's address (LAN, mDNS, Tailscale and so on), never the address itself.
Before measuring, time an ordinary file download on the same network, for example by running python3 -m http.server on a computer and downloading a large file on the phone. That speed is roughly the ceiling of the network. Tuning the settings only matters when this page comes in well below it.
Where it is known to fail¶
Public Wi-Fi usually isolates clients from each other, which is exactly the setting where this would be most useful. On such a network, if the connection has not opened 15 seconds after applying the reply, step 3 says so and suggests a hotspot you run yourself.
Tor Browser disables WebRTC because it leaks your real IP, so that build cannot use this page.
Corporate and some guest networks block multicast, which stops the .local names browsers use to find each other from resolving.
This page sends nothing¶
There is no fetch, XMLHttpRequest, sendBeacon or WebSocket here, and no external resource is loaded. The connection settings carry no STUN and no TURN server, so a connection only happens through candidate addresses inside the same local network.
File contents move only between connected devices. When a device introduces two others, the connection descriptions pass only through that already connected device. On export, IP addresses and mDNS names are masked before anything is written to the file.
Report what you find¶
Before sending, fill in the field in step 5 with this run's setup, for example "own hotspot, Mac Chrome to iPhone Safari". That line rides along with every log entry, which is what makes the results line up later.
The log is kept in this tab and survives a reload. Closing the tab or pressing "Clear log" removes it. Press "Export log", save the JSON, and attach it to issue #553. Export from every device, so that timings from the sending and the receiving side are all there.
Failures are just as useful. One of the criteria is how quickly a failure becomes visible, and three minutes of a spinner is a very different experience from a clear message saying it will not work here.