GitHub - unprovable/ShadowCat: Single file optical file transfer using a browser · GitHub
/" data-turbo-transient="true" />
Skip to content
Search or jump to...
Search code, repositories, users, issues, pull requests...
-->
Search
Clear
Search syntax tips
Provide feedback
--><br>We read every piece of feedback, and take your input very seriously.
Include my email address so I can be contacted
Cancel
Submit feedback
Saved searches
Use saved searches to filter your results more quickly
-->
Name
Query
To see all available qualifiers, see our documentation.
Cancel
Create saved search
Sign in
/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up
Appearance settings
Resetting focus
You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.
Dismiss alert
{{ message }}
unprovable
ShadowCat
Public
Notifications<br>You must be signed in to change notification settings
Fork
Star
main
BranchesTags
Go to file
CodeOpen more actions menu
Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit
History<br>3 Commits<br>3 Commits
README.md
README.md
shadowcat.html
shadowcat.html
View all files
Repository files navigation
ShadowCat
A fully offline, single-file HTML page for moving data between two devices via QR codes — intended for old phones whose radios (BLE, NFC, etc.) arevdead but whose cameras and browsers still work.
Tabs
Generate — encode text into a single QR code.
Scan — decode a single QR via the camera.
Send file — pick a file, choose chunk size / FPS / ECC, hit Start. Cycles through [header, chunk1…chunkN] forever at the chosen FPS. Pause / Resume / Stop.
Start from — begin the loop at a chosen frame index; it then continues forward and wraps back to the header normally.
Show frame + Show / − / + — display exactly one frame static, for resending a specific missing chunk. The number matches the chunk index shown in the receiver's missing-chunks grid (0 = header).
Receive file — start the camera and point at the sender. Header autodetects, progress bar fills in, missing-chunks grid shows which ones haven't arrived yet. When complete, the file's CRC is verified and a Download button appears.
Protocol
Header: QRX1|H||||
Data: QRX1|D|| (1-indexed)
Base64 alphabet has no |, so parsing is just split('|').
Receiver tracks chunks by index, ignores duplicates, dedupes header by CRC.
Practical notes for old phones
Camera needs HTTPS or localhost — file:// won't grant getUserMedia permission. Serve with python3 -m http.server 8000 and visit http://:8000/qrcode.html over the local network. iOS Safari additionally requires HTTPS for cross-device access — for a LAN setup, caddy or a self-signed cert helps.
If render fails on a frame ("code length overflow"), drop chunk size or drop ECC level.
500 chars × 3 fps ≈ 1.1 KB/s base64 ≈ 0.83 KB/s raw. A 100 KB file is roughly 2 minutes per loop; receiver typically needs 1-2 loops.
If old devices struggle to decode: lower FPS, raise ECC to Q, shrink chunk to ~300 chars — produces smaller, less dense QRs.
About
Single file optical file transfer using a browser
Resources
Readme
Uh oh!
There was an error while loading. Please reload this page.
Activity
Stars
stars
Watchers
watching
Forks
forks
Report repository
Releases
No releases published
Packages
Uh oh!
There was an error while loading. Please reload this page.
Contributors
Uh oh!
There was an error while loading. Please reload this page.
Languages
HTML<br>100.0%
You can’t perform that action at this time.