<?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: Toolfyra</title>
    <description>The latest articles on DEV Community by Toolfyra (@toolfyra_bfa754d1768a7068).</description>
    <link>https://dev.to/toolfyra_bfa754d1768a7068</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4194706%2F8adc1978-ad48-4473-945b-9483b85e373a.png</url>
      <title>DEV Community: Toolfyra</title>
      <link>https://dev.to/toolfyra_bfa754d1768a7068</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/toolfyra_bfa754d1768a7068"/>
    <language>en</language>
    <item>
      <title>Merge, Split, Fill, OCR: 6 PDF Jobs You Can Do Free in Your Browser (No Upload, No Signup)</title>
      <dc:creator>Toolfyra</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:08:00 +0000</pubDate>
      <link>https://dev.to/toolfyra_bfa754d1768a7068/merge-split-fill-ocr-6-pdf-jobs-you-can-do-free-in-your-browser-no-upload-no-signup-5a7j</link>
      <guid>https://dev.to/toolfyra_bfa754d1768a7068/merge-split-fill-ocr-6-pdf-jobs-you-can-do-free-in-your-browser-no-upload-no-signup-5a7j</guid>
      <description>&lt;p&gt;The most common lie in the PDF world is the word "free." The tool is free the way a toll road is free the first time — until you hit the paywall at 2 MB, or the watermark, or the "your file has been uploaded to our servers, processing may take up to 1 hour" queue.&lt;/p&gt;

&lt;p&gt;I built a set of PDF tools on &lt;a href="https://toolfyra.com/" rel="noopener noreferrer"&gt;toolfyra.com&lt;/a&gt; that work differently: everything runs in your browser, files never leave your machine, and there's no signup between you and the output. This post covers the six jobs they handle, when each is the right call, and — honestly — when you should use something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Merge PDFs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/merge-pdf" rel="noopener noreferrer"&gt;Merge PDF&lt;/a&gt;&lt;/strong&gt; — Drop in multiple files, drag to reorder, merge into one. This is the tool I use most myself: statement + receipt + invoice into a single expense file. The merging happens locally in your browser using PDF libraries compiled to WebAssembly, which is why there's no file-size "free tier" — your machine is doing the work, not a shared server.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Convert PDF pages to images
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/pdf-to-jpg" rel="noopener noreferrer"&gt;PDF to JPG&lt;/a&gt;&lt;/strong&gt; — Export pages as images, useful for slides you want to drop into a deck or doc, or for sharing a single page in a chat without sending the whole document. One honest caveat: converting to JPG means you lose text selection and searchability. If you need the PDF to stay a PDF, don't convert it — that's a different tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. PDF to PowerPoint
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/pdf-to-ppt" rel="noopener noreferrer"&gt;PDF to PPT&lt;/a&gt;&lt;/strong&gt; — The reverse-rescue operation: someone sent you a PDF of a deck, the original .pptx is gone, and you're expected to "just make a small edit." This gets you an editable-ish PowerPoint back. Set expectations correctly: complex layouts won't survive pixel-perfect, so do a quick visual pass on each slide before you present anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Fill PDF forms
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/pdf-form-filler" rel="noopener noreferrer"&gt;PDF Form Filler&lt;/a&gt;&lt;/strong&gt; — The eternal government-form problem: the PDF has fields, your viewer flattens them, and the instructions say "print, fill by hand, scan back." This tool lets you type into the fields and download the filled result. It has quietly saved me more hours than any other tool I've built.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Add page numbers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/pdf-page-numbers" rel="noopener noreferrer"&gt;PDF Page Numbers&lt;/a&gt;&lt;/strong&gt; — Add page numbers with position and format options. Trivial in Word, weirdly absent from most free PDF flows. Attachments for legal, academic and tender submissions routinely get rejected for missing page numbers, so this is a small tool with outsized "unblock your afternoon" energy.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Extract text with OCR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/ocr-text-extractor" rel="noopener noreferrer"&gt;OCR Text Extractor&lt;/a&gt;&lt;/strong&gt; — When the PDF is a scan (image, not text), this runs OCR locally and gives you the text layer. Quality depends heavily on scan quality — a clean 300 DPI scan extracts beautifully, a phone photo of a receipt at midnight extracts like a phone photo of a receipt at midnight. Garbage in, slightly organized garbage out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "no upload" is the whole point
&lt;/h2&gt;

&lt;p&gt;A surprising number of "free" PDF tools send your file to their servers for processing. For a public menu that's fine. For a contract, an ID scan, a payslip, or a medical report, it's a decision you should make consciously. I wrote a longer piece about what free file converters actually do with your files — the short version: zero-retention is an architecture property, not a checkbox, and the only way to &lt;em&gt;guarantee&lt;/em&gt; a file never leaks is for it to never leave your device. That's the design principle behind this entire set.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use something else
&lt;/h2&gt;

&lt;p&gt;Big, complex, interactive PDF work — live form design, redaction workflows, digital signatures with certificate infrastructure — belongs in professional software. These six tools cover the high-frequency, low-complexity jobs that make up maybe 80% of everyday PDF interactions, and they do it without an account or an upload.&lt;/p&gt;

&lt;p&gt;Everything lives under the &lt;a href="https://toolfyra.com/tools/pdf" rel="noopener noreferrer"&gt;PDF tools hub&lt;/a&gt;, so one bookmark covers the set. If you've ever printed a PDF just to fill it by hand, start with the form filler.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: toolfyra.com is my own project. This post is part of a series about building it — I write about the architecture decisions, the data, and the occasional embarrassing bug.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>productivity</category>
      <category>tools</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Your Files Never Leave the Browser: How Client-Side Tools Actually Work</title>
      <dc:creator>Toolfyra</dc:creator>
      <pubDate>Tue, 20 Oct 2026 19:01:04 +0000</pubDate>
      <link>https://dev.to/toolfyra_bfa754d1768a7068/your-files-never-leave-the-browser-how-client-side-tools-actually-work-8lh</link>
      <guid>https://dev.to/toolfyra_bfa754d1768a7068/your-files-never-leave-the-browser-how-client-side-tools-actually-work-8lh</guid>
      <description>&lt;p&gt;Every "free online file converter" on the internet is secretly one of two different machines. They look identical in search results. Both have a drop zone, a progress bar, and a download button. But under the hood, one is a thin frontend for somebody's server — your file takes a trip, gets processed next to who-knows-what-else, and (if you're lucky) gets deleted "within 24 hours". The other kind is your own computer doing the work, wearing a website as a disguise.&lt;/p&gt;

&lt;p&gt;I run &lt;a href="https://toolfyra.com/" rel="noopener noreferrer"&gt;toolfyra&lt;/a&gt;, a toolkit built entirely as the second kind, and this post is the guided tour of how that architecture actually works: the four browser APIs doing the heavy lifting, where the honest limits are, and — most usefully — how you can verify any tool's "no upload" claim yourself in about thirty seconds with DevTools.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;File.arrayBuffer()&lt;/code&gt; — the file becomes bytes, locally
&lt;/h2&gt;

&lt;p&gt;When a user drops a file onto a page, the browser hands JavaScript a &lt;code&gt;File&lt;/code&gt; object. Crucially, that's a &lt;em&gt;reference to a file on your disk&lt;/em&gt;, not a copy on a server. Reading it into memory is one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// bytes, in RAM, on YOUR machine&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; bytes`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the modern replacement for the old &lt;code&gt;FileReader&lt;/code&gt; + &lt;code&gt;onload&lt;/code&gt; callback dance, and it's the moment every client-side tool begins. Nothing has been transmitted. If the page crashed right here, your file would be exactly where it always was.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Web Workers — heavy work without freezing the tab
&lt;/h2&gt;

&lt;p&gt;Parse a 50 MB PDF on the main thread and the UI locks up: every click, every scroll, dead until the parse finishes. The fix is to move the work into a worker thread — and while you're at it, &lt;em&gt;transfer&lt;/em&gt; the buffer instead of copying it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/js/pdf-worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bufA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bufB&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bufA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bufB&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// transfer list = zero-copy&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;download&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merged.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pdf-worker.js — runs off the main thread, UI stays responsive&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buffers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ignoreEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndices&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;   &lt;span class="c1"&gt;// hand the result back, also zero-copy&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second argument to &lt;code&gt;postMessage&lt;/code&gt; is the transfer list. Without it, structured clone &lt;em&gt;copies&lt;/em&gt; the buffer; with it, ownership moves. For a 500 MB buffer that's the difference between instant and "why is my fan spinning".&lt;/p&gt;

&lt;h2&gt;
  
  
  3. WebAssembly — the part that made this whole category possible
&lt;/h2&gt;

&lt;p&gt;Ten years ago the story would end here, because the browser didn't have real parsers for most file formats. WebAssembly changed the ledger. Battle-tested libraries — compiled from C/C++/Rust or shipped as plain JS — now run in the page's sandbox: &lt;code&gt;pdf-lib&lt;/code&gt; and pdf.js for PDFs, &lt;code&gt;ffmpeg.wasm&lt;/code&gt; for audio and video, &lt;code&gt;Tesseract.js&lt;/code&gt; for OCR. The server-free tool category exists because these ports exist. The same merge engine that powers desktop apps runs in your tab, with no server and no queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. File System Access API + OPFS — when RAM isn't enough
&lt;/h2&gt;

&lt;p&gt;Holding everything in memory works up to a point. For bigger jobs, modern Chromium gives the page streaming access: &lt;code&gt;window.showOpenFilePicker()&lt;/code&gt; returns a handle you can read in chunks, and the Origin Private File System (OPFS) provides a browser-managed scratch space with its own quota. The pattern is feature detection, not user-agent sniffing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showOpenFilePicker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// streaming path: read slices, write chunks to OPFS as you go&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// fallback: ArrayBuffer + Blob, same math, smaller ceiling&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The honest limits
&lt;/h2&gt;

&lt;p&gt;Client-side processing is not magic, and any tool that pretends otherwise is selling something. Memory is the big one: a server streams a 2 GB file; a browser holds it in RAM next to every other tab you have open. Mobile Safari adds its own caps — the File System Access API isn't there yet, and iOS is aggressive about killing pages that allocate too much. GPU-class work (OCR across a 500-page scan, video transcodes) is still often faster on real hardware that isn't your laptop. And "works offline" only applies &lt;em&gt;after&lt;/em&gt; the first load, because the WASM binaries themselves need one cached fetch. Good tools feature-detect, degrade gracefully, and tell you the ceiling instead of pretending there isn't one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the claim yourself: the 30-second audit
&lt;/h2&gt;

&lt;p&gt;This is the part worth bookmarking. "Files never leave your browser" is not a promise you have to believe — it's an architecture claim you can test:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Network tab.&lt;/strong&gt; Open DevTools → Network, filter to Fetch/XHR, run the job. Local processing means no request body ever carries your file. If you see a multi-megabyte POST, you just watched your file leave.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The airplane test.&lt;/strong&gt; Load the tool, disconnect from the network, run a job. If it completes offline, nothing external was involved. (Load the page first — the WASM payload needs one cached fetch.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the source.&lt;/strong&gt; Search the bundle for &lt;code&gt;fetch(&lt;/code&gt;, &lt;code&gt;XMLHttpRequest&lt;/code&gt;, and &lt;code&gt;sendBeacon(&lt;/code&gt;. A local-only tool uses them for assets and analytics, never for file payloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The retention sentence.&lt;/strong&gt; The privacy page should describe what happens to your file in plain words. "Processed locally in your browser" is testable now. "Deleted within 24 hours" is a promise about a server you can't see.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this is the whole point
&lt;/h2&gt;

&lt;p&gt;Zero-retention is an architecture property, not a policy checkbox. For a public menu PDF, upload-based processing is fine. For a contract, an ID scan, a payslip, or a medical report, the only way to &lt;em&gt;guarantee&lt;/em&gt; a file never leaks from a server is for there to be no server in the loop. That's the design principle this entire category — and my toolkit — is built on.&lt;/p&gt;

&lt;p&gt;Everything above is how &lt;a href="https://toolfyra.com/" rel="noopener noreferrer"&gt;toolfyra.com&lt;/a&gt;'s &lt;a href="https://toolfyra.com/tools/pdf" rel="noopener noreferrer"&gt;PDF tools&lt;/a&gt; work: merge, split, fill forms, OCR, page numbers — no accounts, no uploads, and a Network tab that stays boring.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: toolfyra.com is my own project. This post is part of a series about building it in public — the architecture, the data, and the occasional embarrassing bug.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>productivity</category>
    </item>
    <item>
      <title>17 Free Online Tools That Belong in Every Developer's Bookmark Bar (2026)</title>
      <dc:creator>Toolfyra</dc:creator>
      <pubDate>Tue, 20 Oct 2026 12:57:00 +0000</pubDate>
      <link>https://dev.to/toolfyra_bfa754d1768a7068/17-free-online-tools-that-belong-in-every-developers-bookmark-bar-2026-4ja2</link>
      <guid>https://dev.to/toolfyra_bfa754d1768a7068/17-free-online-tools-that-belong-in-every-developers-bookmark-bar-2026-4ja2</guid>
      <description>&lt;p&gt;Every developer has that moment: you need a DNS record checked, a color pair validated, a JSON string eyeballed — and the first three search results are "free tools" that want an account, a credit card, or an upload of whatever you're working on.&lt;/p&gt;

&lt;p&gt;I run &lt;a href="https://toolfyra.com/" rel="noopener noreferrer"&gt;toolfyra.com&lt;/a&gt;, a collection of free browser tools I built partly out of frustration with exactly that pattern. This post is the developer-flavored subset of that collection: 17 tools I keep in my own bookmark bar. Everything runs client-side, nothing asks you to sign up, and I'll note where each tool &lt;em&gt;stops&lt;/em&gt; being the right answer.&lt;/p&gt;

&lt;p&gt;Here's the list, grouped by what you'd actually use them for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Network &amp;amp; DNS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/dns-lookup" rel="noopener noreferrer"&gt;DNS Lookup&lt;/a&gt;&lt;/strong&gt; — Query A, AAAA, MX, TXT, CNAME and NS records for any domain, from your browser. Useful when a DNS change "hasn't propagated" and you want to see what each record actually resolves to, without opening a terminal or hitting a rate-limited web tool that wants your email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/internet-speed-test" rel="noopener noreferrer"&gt;Internet Speed Test&lt;/a&gt;&lt;/strong&gt; — Quick down/up check when a deploy "feels slow" and you want to rule out your own connection before blaming the CI runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design &amp;amp; Frontend
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/color-contrast-checker" rel="noopener noreferrer"&gt;Color Contrast Checker&lt;/a&gt;&lt;/strong&gt; — Paste two colors, get the WCAG contrast ratio and pass/fail for AA/AAA at normal and large text sizes. I built this after one too many "the gray-on-gray looks fine to me" debates. It doesn't feel fine to 8% of your male users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/favicon-generator" rel="noopener noreferrer"&gt;Favicon Generator&lt;/a&gt;&lt;/strong&gt; — Drop in an image, get the icon set sized for modern browsers. Nothing fancy, and that's the point: it does the one boring job and gets out of the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/qr-code-generator" rel="noopener noreferrer"&gt;QR Code Generator&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://toolfyra.com/link-qr-generator" rel="noopener noreferrer"&gt;Link QR Generator&lt;/a&gt;&lt;/strong&gt; — Two flavors of the same job: turn a URL into a scannable QR code locally. Handy for testing mobile flows — generate a QR of your &lt;code&gt;localhost&lt;/code&gt; tunnel URL and scan it instead of typing a 60-character ngrok address on your phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text &amp;amp; Data
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/markdown-previewer" rel="noopener noreferrer"&gt;Markdown Previewer&lt;/a&gt;&lt;/strong&gt; — Paste markdown, see it rendered, copy it back out. Useful for README drafts and dev.to posts themselves, before you commit them anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/text-similarity-checker" rel="noopener noreferrer"&gt;Text Similarity Checker&lt;/a&gt;&lt;/strong&gt; — Compare two blocks of text and get a similarity score. I use it to sanity-check paraphrased docs sections and to make sure two "rewritten" paragraphs aren't secretly the same paragraph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/ai-text-detector" rel="noopener noreferrer"&gt;AI Text Detector&lt;/a&gt;&lt;/strong&gt; — A heuristic check for AI-flavored prose. Treat it as a signal, not a verdict — no detector is a judge — but it's useful for a quick gut-check on copy that reads suspiciously smooth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/ocr-text-extractor" rel="noopener noreferrer"&gt;OCR Text Extractor&lt;/a&gt;&lt;/strong&gt; — Pull text out of screenshots and photos, processed in your browser. Great for "the error message is in this screenshot and I need to grep it."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/reverse-text" rel="noopener noreferrer"&gt;Reverse Text&lt;/a&gt;&lt;/strong&gt; — Exactly what it says. Occasionally the correct tool for a palindrome test, a puzzle, or a very specific kind of debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/text-to-speech" rel="noopener noreferrer"&gt;Text-to-Speech&lt;/a&gt;&lt;/strong&gt; — Paste text, hear it read back. I use it to proofread: my ears catch run-on sentences my eyes skip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everyday Utilities
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/ai-token-counter" rel="noopener noreferrer"&gt;AI Token Counter&lt;/a&gt;&lt;/strong&gt; — Estimate token counts for a prompt before you paste it into an API. Not model-exact, but close enough to know whether you're at 400 tokens or 4,000, which is the difference that actually matters for cost and context limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/pomodoro-timer" rel="noopener noreferrer"&gt;Pomodoro Timer&lt;/a&gt;&lt;/strong&gt; — A clean 25/5 timer in a tab. I wrote about the surprising data behind pomodoro search results before; the tool itself is deliberately boring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/typing-speed-test" rel="noopener noreferrer"&gt;Typing Speed Test&lt;/a&gt;&lt;/strong&gt; — WPM and accuracy. Destructive to your ego, constructive to your PR turnaround time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://toolfyra.com/coin-flip" rel="noopener noreferrer"&gt;Coin Flip&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://toolfyra.com/wheel-picker" rel="noopener noreferrer"&gt;Wheel Picker&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://toolfyra.com/yes-no-picker" rel="noopener noreferrer"&gt;Yes/No Picker&lt;/a&gt;&lt;/strong&gt; — Three tools for the same human problem: decisions that don't deserve a meeting. I keep them grouped because "pick one at random and move on" is a legitimate engineering strategy for reversible choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest fine print
&lt;/h2&gt;

&lt;p&gt;These are single-purpose tools, not platforms. There's no history, no sync, no collaboration — if you need those, you need a real product, and some of these categories have excellent paid ones. What you get instead is: no account, no upload, no "free tier" that changes under you. The files you process and the text you paste are handled in your browser.&lt;/p&gt;

&lt;p&gt;The full catalog is at &lt;a href="https://toolfyra.com/" rel="noopener noreferrer"&gt;toolfyra.com&lt;/a&gt;, organized by category (there's a &lt;a href="https://toolfyra.com/tools/dev" rel="noopener noreferrer"&gt;dev tools&lt;/a&gt; hub that groups most of the above). If you bookmark just one thing, make it the hub — I'd rather you have the whole shelf than one screwdriver.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: toolfyra.com is my own project. This is part of an ongoing series about building it — the earlier posts cover things like shipping ffmpeg.wasm in production and what "runs in your browser" actually means.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>tools</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
