<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Frank</title>
    <description>The latest articles on DEV Community by Frank (@inzidetech).</description>
    <link>https://dev.to/inzidetech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3830097%2Fee3b3c2e-5d3b-475e-a96b-eac775c6dc04.png</url>
      <title>DEV Community: Frank</title>
      <link>https://dev.to/inzidetech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/inzidetech"/>
    <language>en</language>
    <item>
      <title>I built a free P2P device sync tool — no account needed, just scan a QR code</title>
      <dc:creator>Frank</dc:creator>
      <pubDate>Tue, 17 Mar 2026 22:09:45 +0000</pubDate>
      <link>https://dev.to/inzidetech/i-built-a-free-p2p-device-sync-tool-no-account-needed-just-scan-a-qr-code-10af</link>
      <guid>https://dev.to/inzidetech/i-built-a-free-p2p-device-sync-tool-no-account-needed-just-scan-a-qr-code-10af</guid>
      <description>&lt;p&gt;I got tired of emailing files to myself.&lt;/p&gt;

&lt;p&gt;Every time I needed to move a photo from my phone to my laptop I had &lt;br&gt;
to either send it via WhatsApp Web, upload to Google Drive, or email &lt;br&gt;
it to myself. All of these require accounts, have storage limits, and &lt;br&gt;
are honestly overkill for "I just need this file on my other screen &lt;br&gt;
right now."&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;VaultSync&lt;/strong&gt; — (&lt;a href="https://vsyn.cc" rel="noopener noreferrer"&gt;https://vsyn.cc&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The whole concept fits in one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Open the same URL on two devices, scan the QR code, and they're &lt;br&gt;
connected — no account, no app, no waiting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lre6oex6xqpwevkrnzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lre6oex6xqpwevkrnzu.png" alt=" " width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. The QR code encodes a session URL. When Device B scans it, &lt;br&gt;
both browsers join the same WebRTC signaling channel, negotiate a &lt;br&gt;
direct connection, and start syncing.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the connection works
&lt;/h2&gt;

&lt;p&gt;When you open vsyn.cc, the server generates a unique session token &lt;br&gt;
and embeds it in the QR code. &lt;/p&gt;

&lt;p&gt;After the handshake, the signaling server steps out completely. &lt;br&gt;
Data flows directly between browsers using WebRTC Data Channels.&lt;/p&gt;




&lt;h2&gt;
  
  
  Text sync
&lt;/h2&gt;

&lt;p&gt;The textarea syncs in real time across both devices. When you type &lt;br&gt;
on Device A, it saves to the session every few seconds and Device B &lt;br&gt;
polls for updates. Simple but effective for notes, URLs, passwords &lt;br&gt;
and clipboard content.&lt;/p&gt;




&lt;h2&gt;
  
  
  File transfer
&lt;/h2&gt;

&lt;p&gt;For the free tier, files are uploaded to a temporary server slot &lt;br&gt;
(JPG, PNG, PDF up to 10MB, 1 slots). They're deleted automatically &lt;br&gt;
when the session ends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuvnoac823o21gy7zpms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuvnoac823o21gy7zpms.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the receiving end, chunks are collected in an array and assembled &lt;br&gt;
into a Blob when the &lt;code&gt;done&lt;/code&gt; signal arrives:&lt;/p&gt;




&lt;h2&gt;
  
  
  Private Rooms
&lt;/h2&gt;

&lt;p&gt;The free QR session lasts 10 minutes. For something persistent I &lt;br&gt;
built Private Rooms — password-protected encrypted spaces that &lt;br&gt;
survive page refreshes and reconnect automatically.&lt;/p&gt;

&lt;p&gt;A Private Room includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No time limit&lt;/strong&gt; — stays active for 1 year&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple devices&lt;/strong&gt; — connect as many as you want&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice chat&lt;/strong&gt; — WebRTC audio tracks, peer-to-peer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whiteboard&lt;/strong&gt; — real-time collaborative canvas via Socket.IO&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P2P Transfer&lt;/strong&gt; — up to 2GB, direct between browsers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5 file slots&lt;/strong&gt; — 10MB each, persistent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The room credentials are saved in &lt;code&gt;localStorage&lt;/code&gt; so returning to the &lt;br&gt;
URL auto-logs you back in without entering the password again.&lt;/p&gt;




&lt;h2&gt;
  
  
  The voice chat implementation
&lt;/h2&gt;

&lt;p&gt;Adding voice was surprisingly straightforward once the PeerConnection &lt;br&gt;
was already set up for data.&lt;/p&gt;

&lt;p&gt;I use HTTP polling for signaling (instead of a persistent WebSocket &lt;br&gt;
for the RTC negotiation) which keeps the server simpler but adds &lt;br&gt;
~1.5s latency to the initial handshake. Fine for this use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  Privacy tools I added along the way
&lt;/h2&gt;

&lt;p&gt;Since the whole product is about moving data privately, I added two &lt;br&gt;
client-side tools that fit the theme:&lt;/p&gt;

&lt;h3&gt;
  
  
  EXIF Metadata Analyzer
&lt;/h3&gt;

&lt;p&gt;Most people don't realize that photos taken on a smartphone contain &lt;br&gt;
the &lt;strong&gt;exact GPS coordinates&lt;/strong&gt; of where the photo was taken — embedded &lt;br&gt;
silently in the file's EXIF metadata.&lt;/p&gt;

&lt;p&gt;The analyzer reads all hidden fields (GPS, camera model, timestamps, &lt;br&gt;
software, author) and displays them. It also strips everything and &lt;br&gt;
lets you download a clean copy, using a manual JPEG binary parser&lt;/p&gt;

&lt;h3&gt;
  
  
  Background Remover
&lt;/h3&gt;

&lt;p&gt;Uses Google's &lt;strong&gt;MediaPipe Selfie Segmentation&lt;/strong&gt; running via WebGL &lt;br&gt;
in the browser — no server call, no API key, no upload:&lt;/p&gt;

&lt;p&gt;Post-processing sliders let you control edge blur, threshold and &lt;br&gt;
expansion to improve the cutout quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I struggled with
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NAT traversal&lt;/strong&gt; — about 15% of connections fail with STUN alone &lt;br&gt;
because of symmetric NAT. I use a TURN relay as fallback which works &lt;br&gt;
but adds latency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile Safari autoplay&lt;/strong&gt; — iOS Safari blocks &lt;code&gt;audio.play()&lt;/code&gt; without &lt;br&gt;
a direct user gesture. I had to add a tap-to-unmute flow for voice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebRTC renegotiation&lt;/strong&gt; — adding audio tracks after a data channel &lt;br&gt;
is already established requires a full offer/answer cycle. Managing &lt;br&gt;
the signaling state machine to avoid glare conditions took time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large file transfers&lt;/strong&gt; — sending a 2GB file in 64KB chunks over a &lt;br&gt;
Data Channel works, but back-pressure management is critical. Without &lt;br&gt;
the &lt;code&gt;bufferedAmount&lt;/code&gt; check, Chrome would exhaust memory trying to &lt;br&gt;
buffer the entire file.&lt;/p&gt;




&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; PHP (yes, PHP — fast to deploy, works everywhere)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time:&lt;/strong&gt; Socket.IO for whiteboard sync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P2P signaling:&lt;/strong&gt; custom HTTP polling endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice signaling:&lt;/strong&gt; separate HTTP polling (1.5s interval)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; MediaPipe WebGL (background removal)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QR generation:&lt;/strong&gt; qrcode.js (client-side)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero frontend framework&lt;/strong&gt; — vanilla JS throughout&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://vsyn.cc" rel="noopener noreferrer"&gt;vsyn.cc&lt;/a&gt;&lt;/strong&gt; — free, no account needed&lt;/p&gt;

&lt;p&gt;Open it on two devices right now — one on your laptop, one on your &lt;br&gt;
phone. Scan the QR. You're connected in under 3 seconds.&lt;/p&gt;

&lt;p&gt;Private Rooms are $5 one-time for 1 year if you need something &lt;br&gt;
persistent with voice and whiteboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I'm planning to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OCR tool&lt;/strong&gt; — extract text from images, runs in browser via 
Tesseract.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File hash verifier&lt;/strong&gt; — generate SHA-256 checksums client-side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password analyzer&lt;/strong&gt; — entropy + HaveIBeenPwned k-anonymity check&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Would love to hear what you'd find useful. Drop a comment or reach &lt;br&gt;
me at &lt;a href="mailto:soporte@vsyn.cc"&gt;soporte@vsyn.cc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>php</category>
      <category>node</category>
    </item>
  </channel>
</rss>
