<?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: KaiDev</title>
    <description>The latest articles on DEV Community by KaiDev (@kaidev-dev).</description>
    <link>https://dev.to/kaidev-dev</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%2F3855314%2Fad078026-8914-40e6-a10f-450f9f6fc07d.jpeg</url>
      <title>DEV Community: KaiDev</title>
      <link>https://dev.to/kaidev-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kaidev-dev"/>
    <language>en</language>
    <item>
      <title>Building Browser-to-Browser File Transfer with WebRTC: What I Learned Building TransP2P</title>
      <dc:creator>KaiDev</dc:creator>
      <pubDate>Mon, 04 May 2026 09:00:58 +0000</pubDate>
      <link>https://dev.to/kaidev-dev/building-browser-to-browser-file-transfer-with-webrtc-what-i-learned-building-transp2p-kfc</link>
      <guid>https://dev.to/kaidev-dev/building-browser-to-browser-file-transfer-with-webrtc-what-i-learned-building-transp2p-kfc</guid>
      <description>&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%2Fteylml02izuoqch8gsms.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%2Fteylml02izuoqch8gsms.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built a P2P file transfer tool that runs entirely in the browser. No install, no server relay, no account. Here's what I learned about WebRTC data channels, resumable transfers, and the browser storage mess along the way.&lt;/p&gt;




&lt;p&gt;Most file transfer tools follow the same pattern: upload to a server, get a link, the recipient downloads from that server. Your file sits on someone else's infrastructure, at least for a while.&lt;/p&gt;

&lt;p&gt;I wanted to try a different approach: what if the file never leaves the browser at all?&lt;/p&gt;

&lt;p&gt;That question turned into &lt;a href="https://transp2p.com" rel="noopener noreferrer"&gt;TransP2P&lt;/a&gt; — a browser-based P2P file transfer tool built on WebRTC. In this post I'll walk through the technical decisions, the parts that were harder than expected, and the two browser storage paths that almost no one talks about.&lt;/p&gt;




&lt;h2&gt;
  
  
  The basic architecture
&lt;/h2&gt;

&lt;p&gt;The flow is straightforward in theory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Both devices open the site (no install, no account).&lt;/li&gt;
&lt;li&gt;A WebSocket signaling server helps them exchange SDP offers/answers and ICE candidates.&lt;/li&gt;
&lt;li&gt;A direct WebRTC data channel is established.&lt;/li&gt;
&lt;li&gt;The file is chunked and sent over the data channel.&lt;/li&gt;
&lt;li&gt;The receiver reassembles the chunks and saves the file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The signaling server never touches the file data. Once the connection is established, it's just helping with connection maintenance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chunking everything — even when you don't need to
&lt;/h2&gt;

&lt;p&gt;One early decision that paid off: &lt;strong&gt;every file is chunked during transfer&lt;/strong&gt;, regardless of whether resumable transfer is enabled.&lt;/p&gt;

&lt;p&gt;The alternative — sending a file as one blob — seems simpler but creates problems fast. Large files block the main thread, and any network hiccup means starting over.&lt;/p&gt;

&lt;p&gt;With chunking, each piece is typically 64 KB. The sender streams chunks over the data channel; the receiver buffers them in memory and writes to disk on download. This gives the best speed because there's no intermediate disk I/O during the transfer itself — the bottleneck is the data channel throughput, not your disk.&lt;/p&gt;




&lt;h2&gt;
  
  
  The resumable transfer problem (and why there are two solutions)
&lt;/h2&gt;

&lt;p&gt;Resumable transfer was where things got interesting — and where browser differences forced two completely separate implementations.&lt;/p&gt;

&lt;h3&gt;
  
  
  The goal
&lt;/h3&gt;

&lt;p&gt;If a transfer is interrupted (network drops, user closes the tab), the receiver should be able to resume from where it left off, not start from zero.&lt;/p&gt;

&lt;p&gt;The core idea is simple: &lt;strong&gt;track which chunks have been received, and tell the sender to start from chunk N on reconnect.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But &lt;em&gt;where&lt;/em&gt; you store those chunks is the hard part, because browsers don't give you raw disk access. And they definitely don't all give you the same APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Path 1: OPFS + File System Access API (Chrome, Edge, Firefox)
&lt;/h3&gt;

&lt;p&gt;Modern Chromium-based browsers and newer Firefox versions support the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system" rel="noopener noreferrer"&gt;Origin Private File System (OPFS)&lt;/a&gt;. OPFS lets you write files in a sandboxed origin-private directory, and it's fast — writes happen off the main thread when used with Web Workers.&lt;/p&gt;

&lt;p&gt;My implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File chunks&lt;/strong&gt; are written directly into OPFS using a writable stream from a Web Worker. This bypasses the browser's memory limit entirely — you can handle files that are gigabytes in size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File metadata&lt;/strong&gt; (chunk count, received chunk indices, file name, total size) is stored in &lt;strong&gt;IndexedDB&lt;/strong&gt;, because it's small, structured, and queryable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On reconnection, the receiver queries IndexedDB to check whether the transfer was completed for that file. If not, it sends the last successfully received chunk index to the sender via the signaling channel. The sender skips ahead and starts from that chunk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Path 2: IndexedDB only (Safari / iOS)
&lt;/h3&gt;

&lt;p&gt;Safari does not support OPFS access from Web Workers, and the File System Access API is entirely unavailable on iOS. That means you can't stream to disk off the main thread.&lt;/p&gt;

&lt;p&gt;For Safari users, the fallback is &lt;strong&gt;IndexedDB for everything&lt;/strong&gt; — both the file chunks and the metadata.&lt;/p&gt;

&lt;p&gt;This works, but it has a ceiling. IndexedDB stores data in memory before flushing to disk, so very large files eventually hit Safari's memory limit. On iOS in particular, the limits are strict — file size is effectively capped, and the experience degrades gracefully by showing a size warning before the transfer starts.&lt;/p&gt;

&lt;p&gt;It's not ideal, but it's honest. The tool tells you what your browser can and can't do, rather than failing silently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Signaling: keep it minimal
&lt;/h2&gt;

&lt;p&gt;The signaling server's only job is to relay SDP and ICE candidates between peers until the WebRTC connection is established. I use &lt;strong&gt;WebSocket&lt;/strong&gt; for this — it's simple, widely supported, and low-latency.&lt;/p&gt;

&lt;p&gt;The server doesn't authenticate users (no account needed), doesn't store messages beyond the active session, and doesn't know what's being transferred. Once the data channel opens, the signaling server becomes irrelevant to the transfer.&lt;/p&gt;

&lt;p&gt;For production, you do need STUN/TURN servers so peers behind NAT can connect. I use a combination of public STUN servers and a self-hosted TURN server for scenarios where UDP is blocked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bypassing browser memory limits
&lt;/h2&gt;

&lt;p&gt;This is the part that surprised me most: &lt;strong&gt;the data channel itself doesn't have a memory problem — the receiver's storage strategy does.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you receive a 2 GB file and try to reassemble it in an &lt;code&gt;ArrayBuffer&lt;/code&gt;, you'll crash the browser. The key is streaming the incoming chunks &lt;em&gt;directly&lt;/em&gt; to storage (OPFS or IndexedDB) without holding the whole file in memory.&lt;/p&gt;

&lt;p&gt;With OPFS + a Web Worker, this works beautifully — the main thread stays responsive, and the file never exists as a single in-memory object. This is how &lt;a href="https://transp2p.com" rel="noopener noreferrer"&gt;TransP2P&lt;/a&gt; can handle multi-gigabyte files on supported browsers.&lt;/p&gt;

&lt;p&gt;Safari and iOS Safari can't do this, which brings us back to the two-path problem. It's a good reminder that "works in the browser" and "works equally across all browsers" are two different statements.&lt;/p&gt;




&lt;h2&gt;
  
  
  Async mode: when P2P isn't possible
&lt;/h2&gt;

&lt;p&gt;P2P is great when both peers are online simultaneously. But sometimes you want to send a file to someone who isn't online yet, or the connection keeps failing.&lt;/p&gt;

&lt;p&gt;For this, TransP2P has an &lt;strong&gt;optional server cache mode&lt;/strong&gt;. You upload the file to the server (encrypted, with a randomly generated key that's never sent to the server), get a download link or code, and the recipient retrieves it later. Files expire automatically — either after a set time or after a download count is reached.&lt;/p&gt;

&lt;p&gt;This is a paid feature because it consumes server resources, but the encryption design means the server never sees the file contents. The encryption key is derived client-side and embedded in the download link fragment (the part after &lt;code&gt;#&lt;/code&gt;), so it never reaches the server at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  Things I wish I knew earlier
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ICE candidate gathering takes time.&lt;/strong&gt; Don't assume the connection is stuck — it often just needs a few more seconds. Show feedback to the user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile browsers are not desktop browsers.&lt;/strong&gt; iOS Safari has the tightest restrictions of any modern browser. Test on it early, not after launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data channel reliability vs speed is a tradeoff.&lt;/strong&gt; &lt;code&gt;ordered: true&lt;/code&gt; gives you guaranteed ordering but can stall on packet loss. For file transfer, I went with &lt;code&gt;ordered: true&lt;/code&gt; and chunk-level checksumming — the stall is rare in practice and ordering bugs are worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;People don't understand "P2P".&lt;/strong&gt; The biggest UX challenge wasn't technical — it was explaining why the file transfers without uploading. If you build something similar, spend time on the "how it works" copy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it / See the code
&lt;/h2&gt;

&lt;p&gt;TransP2P is free to use (the P2P mode is entirely free; server cache is paid). No account, no install:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://transp2p.com" rel="noopener noreferrer"&gt;https://transp2p.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The site also has a few longer reads on &lt;a href="https://transp2p.com/blog/tutorial" rel="noopener noreferrer"&gt;how P2P file transfer actually works&lt;/a&gt; and &lt;a href="https://transp2p.com/blog/file-transfer-privacy" rel="noopener noreferrer"&gt;the hidden privacy risks of cloud-based file transfer&lt;/a&gt; — written for a general audience if you're curious about the non-technical side of this.&lt;/p&gt;

&lt;p&gt;If you've built something with WebRTC data channels, I'd love to hear what tradeoffs you made — particularly around large file handling. The browser storage landscape is still messy in 2026, and more field reports would help everyone.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post was originally written by the author of TransP2P. If you're working on P2P tools or browser-based file transfer, feel free to reach out.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webrtc</category>
      <category>privacy</category>
    </item>
    <item>
      <title>I built an AI Image Upscaler that runs 100% in the browser (no server uploads)</title>
      <dc:creator>KaiDev</dc:creator>
      <pubDate>Wed, 08 Apr 2026 08:50:21 +0000</pubDate>
      <link>https://dev.to/kaidev-dev/i-built-an-ai-image-upscaler-that-runs-100-in-the-browser-no-server-uploads-42dj</link>
      <guid>https://dev.to/kaidev-dev/i-built-an-ai-image-upscaler-that-runs-100-in-the-browser-no-server-uploads-42dj</guid>
      <description>&lt;p&gt;I’ve been building AI tools for the web, and I just launched a lightweight AI image upscaler that works entirely in your browser.&lt;/p&gt;

&lt;p&gt;The main goal was simple: &lt;strong&gt;privacy + speed + no bloat&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✨ Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;100% local in-browser processing (your images never leave your device)&lt;/li&gt;
&lt;li&gt;Upscale photos, anime, and digital art up to 4K resolution&lt;/li&gt;
&lt;li&gt;No sign-up required, no watermarks on free outputs&lt;/li&gt;
&lt;li&gt;3 free daily uses for everyone&lt;/li&gt;
&lt;li&gt;Clean, fast UI with zero unnecessary bloat&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: Astro + TypeScript + Preact&lt;/li&gt;
&lt;li&gt;AI Runtime: TensorFlow.js / ONNX Runtime Web&lt;/li&gt;
&lt;li&gt;Models: Optimized real-world AI upscaling models for web performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re into AI on the web, browser-side machine learning, or just need a private, no-fuss tool to enhance your photos, give it a try. I’d love to hear your feedback and feature requests!&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Try it here&lt;/strong&gt;: &lt;a href="https://imgupscaleai.com" rel="noopener noreferrer"&gt;https://imgupscaleai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>imageprocessing</category>
      <category>tools</category>
    </item>
    <item>
      <title>I Built an AI Image Upscaler — Welcome Any Feedback</title>
      <dc:creator>KaiDev</dc:creator>
      <pubDate>Fri, 03 Apr 2026 10:48:20 +0000</pubDate>
      <link>https://dev.to/kaidev-dev/i-built-an-ai-image-upscaler-welcome-any-feedback-3pen</link>
      <guid>https://dev.to/kaidev-dev/i-built-an-ai-image-upscaler-welcome-any-feedback-3pen</guid>
      <description>&lt;p&gt;Hi everyone 👋&lt;/p&gt;

&lt;p&gt;I’m a solo indie developer building lightweight, browser-based AI tools.&lt;/p&gt;

&lt;p&gt;Lately I’ve been working on an AI image upscaler — something I built to fix a really common problem:&lt;br&gt;
blurry small images, old photos, compressed screenshots, and pictures you just can’t safely upload to third-party servers.&lt;/p&gt;

&lt;p&gt;What it helps you do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean up and sharpen blurry screenshots, old photos, and low-res social images&lt;/li&gt;
&lt;li&gt;Enlarge small pictures without pixelation&lt;/li&gt;
&lt;li&gt;Fix compression artifacts from repeated saving or messaging apps&lt;/li&gt;
&lt;li&gt;Process sensitive or private images safely, all within your browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tool is made for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content creators and social media users who need clean, clear images for posts&lt;/li&gt;
&lt;li&gt;People looking to restore and digitize old family photos&lt;/li&gt;
&lt;li&gt;Professionals working on presentations, resumes, and reports with blurry assets&lt;/li&gt;
&lt;li&gt;Online sellers who need to enhance product images quickly&lt;/li&gt;
&lt;li&gt;Anyone who values privacy and doesn’t want to upload personal files externally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s simple, fast, and designed for real daily use cases — not overbuilt for professional studios.&lt;/p&gt;

&lt;p&gt;You can check it out here: &lt;a href="https://imgupscaleai.com" rel="noopener noreferrer"&gt;https://imgupscaleai.com&lt;/a&gt;&lt;br&gt;
I’m here to share my progress, learn from other builders, and get feedback.&lt;br&gt;
Nice to meet you all!&lt;/p&gt;

&lt;h1&gt;
  
  
  introduction #indiedev #ai #webdev #tools #contentcreation
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
