<?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: göktürk kahriman</title>
    <description>The latest articles on DEV Community by göktürk kahriman (@gktrk_kahriman_192cb6da).</description>
    <link>https://dev.to/gktrk_kahriman_192cb6da</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%2F3868315%2Fe42dc2f0-8666-45ef-b614-be92df587f4d.jpeg</url>
      <title>DEV Community: göktürk kahriman</title>
      <link>https://dev.to/gktrk_kahriman_192cb6da</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gktrk_kahriman_192cb6da"/>
    <language>en</language>
    <item>
      <title>Why I’m Building a Browser-Native Productivity Platform for File Workflows</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Wed, 15 Apr 2026 11:41:33 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/why-im-building-a-browser-native-productivity-platform-for-file-workflows-2blf</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/why-im-building-a-browser-native-productivity-platform-for-file-workflows-2blf</guid>
      <description>&lt;p&gt;Most online tools solve only one step.&lt;/p&gt;

&lt;p&gt;You upload a file.&lt;br&gt;
You convert it.&lt;br&gt;
You download it.&lt;br&gt;
Then you realize you still need another tool.&lt;/p&gt;

&lt;p&gt;Maybe the PDF is too large.&lt;br&gt;
Maybe it needs a signature.&lt;br&gt;
Maybe it should be protected with a password.&lt;br&gt;
Maybe you need to convert it again.&lt;/p&gt;

&lt;p&gt;So you open another website.&lt;/p&gt;

&lt;p&gt;That experience is broken.&lt;/p&gt;

&lt;p&gt;This is one of the reasons I’m building Kreotar&lt;br&gt;
, a browser-based productivity platform for PDFs, documents, images, converters, and connected workflows.&lt;/p&gt;

&lt;p&gt;The Problem With Traditional Online Tools&lt;/p&gt;

&lt;p&gt;Most file tools are built like isolated utilities.&lt;/p&gt;

&lt;p&gt;They are useful, but they usually stop too early.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Compress a PDF&lt;br&gt;
Download it&lt;br&gt;
Open another tool&lt;br&gt;
Upload it again&lt;br&gt;
Sign it&lt;br&gt;
Download it again&lt;br&gt;
Open another tool&lt;br&gt;
Protect it&lt;br&gt;
Download the final version&lt;/p&gt;

&lt;p&gt;That is not a workflow.&lt;/p&gt;

&lt;p&gt;That is friction.&lt;/p&gt;

&lt;p&gt;A better experience should feel more like this:&lt;/p&gt;

&lt;p&gt;Open file → fix problem → continue next step → export final result&lt;/p&gt;

&lt;p&gt;That is the direction I believe online productivity tools are moving toward.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Why Browser-Based Tools Are Becoming More Powerful&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
The browser is no longer just a place to read websites.&lt;/p&gt;

&lt;p&gt;Modern browsers can handle serious productivity tasks:&lt;/p&gt;

&lt;p&gt;PDF editing&lt;br&gt;
image processing&lt;br&gt;
file conversion&lt;br&gt;
document creation&lt;br&gt;
compression&lt;br&gt;
signing&lt;br&gt;
annotations&lt;br&gt;
local processing&lt;br&gt;
workflow automation&lt;/p&gt;

&lt;p&gt;This creates a huge opportunity.&lt;/p&gt;

&lt;p&gt;Instead of forcing users to install heavy software or jump between multiple websites, more tasks can happen directly inside the browser.&lt;/p&gt;

&lt;p&gt;That is the foundation behind Kreotar&lt;br&gt;
.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Privacy Should Be Part of the Product&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
When users work with files, trust matters.&lt;/p&gt;

&lt;p&gt;A PDF might contain a contract.&lt;br&gt;
A document might contain private notes.&lt;br&gt;
An image might be personal.&lt;br&gt;
A business report might contain sensitive information.&lt;/p&gt;

&lt;p&gt;Not every file should be uploaded somewhere just to perform a simple action.&lt;/p&gt;

&lt;p&gt;That is why browser-based and local-first workflows matter.&lt;/p&gt;

&lt;p&gt;The technical explanation is simple:&lt;/p&gt;

&lt;p&gt;If the browser can process the file safely, the user should not need unnecessary server-side friction.&lt;/p&gt;

&lt;p&gt;But the user-facing explanation is even simpler:&lt;/p&gt;

&lt;p&gt;Your file should feel fast, safe, and under your control.&lt;/p&gt;

&lt;p&gt;Tools Are Useful. Workflows Are More Valuable.&lt;/p&gt;

&lt;p&gt;A single tool can solve a small problem.&lt;/p&gt;

&lt;p&gt;A workflow can solve the full job.&lt;/p&gt;

&lt;p&gt;That is the difference I’m focusing on while building &lt;a href="https://kreotar.com/en" rel="noopener noreferrer"&gt;Kreotar.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Compress PDF → Open in &lt;a href="https://kreotar.com/en/tools/office/kreopdf" rel="noopener noreferrer"&gt;KreoPDF&lt;/a&gt; → Sign → Protect → Download&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Image to PDF → Crop → Compress → Open in KreoPDF → Export&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Document creation → Export PDF → Sign → Share&lt;/p&gt;

&lt;p&gt;The goal is not only to provide many tools.&lt;/p&gt;

&lt;p&gt;The goal is to make those tools work together.&lt;/p&gt;

&lt;p&gt;What I’m Learning as a Founder&lt;/p&gt;

&lt;p&gt;One lesson became clear very quickly:&lt;/p&gt;

&lt;p&gt;Technical features are not enough.&lt;/p&gt;

&lt;p&gt;As developers, we often say things like:&lt;/p&gt;

&lt;p&gt;local-first&lt;br&gt;
browser-native&lt;br&gt;
AI-assisted&lt;br&gt;
client-side processing&lt;br&gt;
workflow-driven&lt;/p&gt;

&lt;p&gt;But users usually ask a much simpler question:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Why should I care?&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
So I’m learning to translate technical ideas into real outcomes:&lt;/p&gt;

&lt;p&gt;Technical Idea  User Benefit&lt;br&gt;
Browser-native  No installation&lt;br&gt;
Local-first Better privacy&lt;br&gt;
Connected tools Less tab switching&lt;br&gt;
AI-assisted Less manual work&lt;br&gt;
Workflows   Finish tasks faster&lt;/p&gt;

&lt;p&gt;That shift matters.&lt;/p&gt;

&lt;p&gt;A product is not valuable because it sounds advanced.&lt;/p&gt;

&lt;p&gt;It is valuable when it helps someone finish their work faster.&lt;/p&gt;

&lt;p&gt;The Future of File Tools&lt;/p&gt;

&lt;p&gt;I think the future of online tools will not be just “more tools.”&lt;/p&gt;

&lt;p&gt;It will be:&lt;/p&gt;

&lt;p&gt;faster&lt;br&gt;
simpler&lt;br&gt;
more private&lt;br&gt;
more connected&lt;br&gt;
easier to use&lt;br&gt;
workflow-oriented&lt;br&gt;
accessible globally&lt;/p&gt;

&lt;p&gt;People do not want to think about which tool they need next.&lt;/p&gt;

&lt;p&gt;They want the next step to be obvious.&lt;/p&gt;

&lt;p&gt;That is what I’m trying to build with Kreotar&lt;br&gt;
.&lt;/p&gt;

&lt;p&gt;A user should be able to arrive for one simple task and continue naturally until the work is finished.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Final Thought&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
The best products reduce friction.&lt;/p&gt;

&lt;p&gt;They do not make users feel like they are operating software.&lt;/p&gt;

&lt;p&gt;They make users feel like they are getting work done.&lt;/p&gt;

&lt;p&gt;That is the product direction I’m focused on:&lt;/p&gt;

&lt;p&gt;Not just online tools.&lt;br&gt;
Connected browser-based workflows.&lt;/p&gt;

&lt;p&gt;If you work with PDFs, documents, images, or file conversions, you can try Kreotar here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;kreotar&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
I’m still building and improving it every day.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
    <item>
      <title>The Browser Is Becoming the New Office Suite</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Tue, 14 Apr 2026 06:41:33 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/the-browser-is-becoming-the-new-office-suite-4ohp</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/the-browser-is-becoming-the-new-office-suite-4ohp</guid>
      <description>&lt;p&gt;For years, online tools followed the same pattern:&lt;/p&gt;

&lt;p&gt;Upload a file.&lt;br&gt;
Wait.&lt;br&gt;
Convert it.&lt;br&gt;
Download it.&lt;br&gt;
Leave.&lt;/p&gt;

&lt;p&gt;It worked, but it never felt complete.&lt;/p&gt;

&lt;p&gt;Today, the way we work with documents is changing. People do not just want a PDF compressor, a converter, or a simple editor. They want to finish the entire task without jumping between five different websites.&lt;/p&gt;

&lt;p&gt;That is the idea behind Kreotar.&lt;/p&gt;

&lt;p&gt;Kreotar is being built as a browser-native productivity ecosystem where users can work with PDFs, documents, images, converters, and future AI workflows in one connected environment.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Why Browser-Based Tools Matter&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
The browser is no longer just a place to read websites.&lt;/p&gt;

&lt;p&gt;Modern browsers can now handle serious productivity tasks:&lt;/p&gt;

&lt;p&gt;editing PDFs&lt;br&gt;
creating documents&lt;br&gt;
compressing files&lt;br&gt;
converting formats&lt;br&gt;
signing documents&lt;br&gt;
annotating pages&lt;br&gt;
working with images&lt;br&gt;
running local workflows&lt;/p&gt;

&lt;p&gt;This opens a powerful opportunity.&lt;/p&gt;

&lt;p&gt;Instead of forcing users to install heavy software or upload sensitive files to unknown servers, many tasks can happen directly inside the browser.&lt;/p&gt;

&lt;p&gt;That means faster access, less friction, and a better privacy experience.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Privacy Is Becoming a Product Feature&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Most people do not think about privacy until they upload something important.&lt;/p&gt;

&lt;p&gt;A contract.&lt;br&gt;
An invoice.&lt;br&gt;
A legal document.&lt;br&gt;
A personal file.&lt;br&gt;
A business report.&lt;/p&gt;

&lt;p&gt;At that moment, trust matters.&lt;/p&gt;

&lt;p&gt;A good online tool should not only be fast. It should also make the user feel safe.&lt;/p&gt;

&lt;p&gt;That is why Kreotar is focused on a privacy-first experience. Many tools are designed to work directly in the browser, helping users complete tasks without unnecessary server-side processing.&lt;/p&gt;

&lt;p&gt;The future of productivity is not only about more features.&lt;/p&gt;

&lt;p&gt;It is about trust.&lt;/p&gt;

&lt;p&gt;From Single Tools to Connected Workflows&lt;/p&gt;

&lt;p&gt;A common problem with online tools is that they solve only one step.&lt;/p&gt;

&lt;p&gt;You compress a PDF, then realize you need to edit it.&lt;br&gt;
You edit a document, then need to convert it.&lt;br&gt;
You sign a PDF, then need to protect it.&lt;br&gt;
You extract data, then need to organize it into a spreadsheet.&lt;/p&gt;

&lt;p&gt;This creates friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar&lt;/a&gt;&lt;/strong&gt; is designed around a different idea:&lt;/p&gt;

&lt;p&gt;Every tool should lead naturally to the next useful action.&lt;/p&gt;

&lt;p&gt;For example, after editing a PDF, a user should be able to compress it, crop it, protect it, standardize pages, or open it inside a dedicated PDF workspace without starting from zero.&lt;/p&gt;

&lt;p&gt;This is where simple tools become a real productivity system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KreoPDF and KreoDoc: The Core of the Ecosystem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two important parts of &lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar’s&lt;/a&gt; direction are KreoPDF and KreoDoc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kreotar.com/en/tools/office/kreopdf" rel="noopener noreferrer"&gt;KreoPDF&lt;/a&gt; is being developed as a powerful browser-based PDF editor for editing, signing, annotating, organizing, compressing, and finalizing PDF files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kreotar.com/en/office/kreodoc" rel="noopener noreferrer"&gt;KreoDoc&lt;/a&gt; is focused on professional document creation and editing inside the browser.&lt;/p&gt;

&lt;p&gt;Together, they represent the bigger vision:&lt;/p&gt;

&lt;p&gt;Not just converting files.&lt;/p&gt;

&lt;p&gt;Actually working with them.&lt;/p&gt;

&lt;p&gt;The goal is to make the browser feel like a lightweight office environment — fast, accessible, and connected.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Why Free Tools Still Matter&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Some people say AI will replace online tools.&lt;/p&gt;

&lt;p&gt;I see it differently.&lt;/p&gt;

&lt;p&gt;AI will not remove the need for useful tools. It will make those tools smarter.&lt;/p&gt;

&lt;p&gt;People will still need to edit PDFs, create documents, convert files, compress images, sign contracts, and prepare professional outputs.&lt;/p&gt;

&lt;p&gt;The difference is that AI can help connect those actions.&lt;/p&gt;

&lt;p&gt;Instead of only asking, “What tool do I need?” users will be able to say:&lt;/p&gt;

&lt;p&gt;“Extract the data from this PDF and organize it into a spreadsheet.”&lt;/p&gt;

&lt;p&gt;“Turn this document into a professional report.”&lt;/p&gt;

&lt;p&gt;“Prepare this file for sending to my client.”&lt;/p&gt;

&lt;p&gt;That is the direction productivity software is moving toward.&lt;/p&gt;

&lt;p&gt;Tools are not disappearing.&lt;/p&gt;

&lt;p&gt;They are becoming workflows.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The Vision for Kreotar&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Kreotar is still early, but the direction is clear:&lt;/p&gt;

&lt;p&gt;A global, browser-native productivity platform with:&lt;/p&gt;

&lt;p&gt;PDF tools&lt;br&gt;
document tools&lt;br&gt;
image tools&lt;br&gt;
file converters&lt;br&gt;
AI-assisted workflows&lt;br&gt;
privacy-first processing&lt;br&gt;
connected studios&lt;br&gt;
future team and API features&lt;/p&gt;

&lt;p&gt;The goal is not to build another random tool directory.&lt;/p&gt;

&lt;p&gt;The goal is to build a system where users can start with one small task and naturally continue until the whole job is finished.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Final Thought&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
The future of productivity software may not be one giant application.&lt;/p&gt;

&lt;p&gt;It may be a collection of fast, focused, connected tools that work together beautifully inside the browser.&lt;/p&gt;

&lt;p&gt;That is what I am building with Kreotar.&lt;/p&gt;

&lt;p&gt;A simpler way to work with files.&lt;br&gt;
A faster way to finish document tasks.&lt;br&gt;
A more private way to use online tools.&lt;/p&gt;

&lt;p&gt;Try it here:&lt;/p&gt;

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

</description>
      <category>kreotar</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Why I Stopped Installing CLI Tools for File Processing (And Started Using WebAssembly)</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Mon, 13 Apr 2026 06:00:00 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/why-i-stopped-installing-cli-tools-for-file-processing-and-started-using-webassembly-3mkm</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/why-i-stopped-installing-cli-tools-for-file-processing-and-started-using-webassembly-3mkm</guid>
      <description>&lt;h1&gt;
  
  
  Why I Stopped Installing CLI Tools for File Processing (And Started Using WebAssembly)
&lt;/h1&gt;

&lt;p&gt;Last month I deleted &lt;code&gt;node_modules&lt;/code&gt; from a project that only needed to merge three PDFs. The folder was 847MB. For a task that took 0.4 seconds to complete.&lt;/p&gt;

&lt;p&gt;We've normalized this insanity. We install gigabytes of dependencies, fight version conflicts, and audit security vulnerabilities just to perform basic file operations. All because we accepted that "real" processing requires "real" software—installed, maintained, and constantly updated.&lt;/p&gt;

&lt;p&gt;I stopped accepting it. Here's what replaced 264 CLI tools in my workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The WebAssembly Tipping Point
&lt;/h2&gt;

&lt;p&gt;WebAssembly (WASM) hit 3.0 in December 2025 [^3^]. It now runs on 5.5% of all websites visited by Chrome users—and that number is accelerating [^11^]. But most developers still associate it with browser games or C++ ports. They miss the infrastructure shift.&lt;/p&gt;

&lt;p&gt;Modern browsers can execute near-native code using your device's actual hardware. Not emulated. Not virtualized. Your GPU, your RAM, your CPU cores—running FFmpeg, ImageMagick, and PDF engines directly in a sandboxed tab [^2^][^8^].&lt;/p&gt;

&lt;p&gt;The implications are architectural, not incremental.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Client-Side" Actually Means
&lt;/h2&gt;

&lt;p&gt;When I say "browser-based," developers assume I mean "uploads to a server, then downloads the result." That's the old model. I'm talking about zero-upload processing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No network latency&lt;/strong&gt;: 47 images compress in 12 seconds, not 12 minutes waiting for cloud queues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No server dependency&lt;/strong&gt;: Works offline on airplanes, in coffee shops, or air-gapped facilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No data exposure&lt;/strong&gt;: Files never leave your device because they never need to [^9^]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The security model is elimination, not protection. When there's no server, there's nothing to breach. This isn't privacy by policy—it's privacy by architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing My Entire Toolkit
&lt;/h2&gt;

&lt;p&gt;I migrated my workflow to &lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar&lt;/a&gt;, a suite of 264+ utilities running entirely in browser sandboxes. Here's what actually changed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PDF Workflows&lt;/strong&gt;: Merging, splitting, and compressing used to require &lt;code&gt;pdftk&lt;/code&gt;, &lt;code&gt;qpdf&lt;/code&gt;, or cloud services with questionable terms. Now: drag, process locally, download. KreoPDF handles form creation, digital signatures, and OCR without a single HTTP request to external servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Image Processing&lt;/strong&gt;: Batch operations that needed ImageMagick or Sharp now run via WebGL-accelerated WASM. 20MB HEIC files from iPhones compress to web-ready formats using my device's GPU, not AWS Lambda.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer Utilities&lt;/strong&gt;: JSON formatting, regex testing, Base64 encoding—operations that sent sensitive data to "free" websites now execute in isolated Web Workers. My logs stay in my browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document Editing&lt;/strong&gt;: KreoDoc replaced my Word dependency for collaborative work. Real-time editing without forcing IP through Microsoft's cloud. WebAssembly speed opens 100-page documents in under a second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Performance Reality
&lt;/h2&gt;

&lt;p&gt;I benchmarked the same operations across three approaches:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Cloud Tool&lt;/th&gt;
&lt;th&gt;Local CLI&lt;/th&gt;
&lt;th&gt;WebAssembly (Browser)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merge 12 PDFs&lt;/td&gt;
&lt;td&gt;45s + upload&lt;/td&gt;
&lt;td&gt;2.1s&lt;/td&gt;
&lt;td&gt;0.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compress 47 images&lt;/td&gt;
&lt;td&gt;8 min queue&lt;/td&gt;
&lt;td&gt;14s&lt;/td&gt;
&lt;td&gt;12s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convert 4K video&lt;/td&gt;
&lt;td&gt;12 min&lt;/td&gt;
&lt;td&gt;3.2s&lt;/td&gt;
&lt;td&gt;2.9s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex test on 10MB log&lt;/td&gt;
&lt;td&gt;3s (upload)&lt;/td&gt;
&lt;td&gt;0.1s&lt;/td&gt;
&lt;td&gt;0.012s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The browser wins because it removes the network hop. Your M-series Mac or Ryzen laptop isn't waiting for a Virginia data center—it &lt;em&gt;is&lt;/em&gt; the data center [^10^].&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Development Workflows
&lt;/h2&gt;

&lt;p&gt;We tell users to "install our app" or "use our API" without questioning if either is necessary. WebAssembly challenges that assumption:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For DevOps&lt;/strong&gt;: Format conversion without spinning up containers. No Dockerfiles for one-off tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Frontend Teams&lt;/strong&gt;: Image optimization without configuring Sharp or maintaining build pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Security&lt;/strong&gt;: File analysis without trusting third-party services with proprietary data. Your client's confidential documents never touch infrastructure you don't control.&lt;/p&gt;

&lt;p&gt;The browser has become a legitimate runtime environment. Not a document viewer. Not a JavaScript sandbox. A hardware-accelerated, secure execution environment that 4.9 billion people already have installed [^9^].&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Pattern: Edge-Heavy Computing
&lt;/h2&gt;

&lt;p&gt;We're witnessing the pendulum swing back from cloud-centralized to client-heavy processing [^4^][^10^]. Not because developers miss local installations, but because browsers matured into runtime environments capable of executing complex algorithms faster than 2015-era desktops.&lt;/p&gt;

&lt;p&gt;The next generation of tools won't require &lt;code&gt;npm install&lt;/code&gt;. They'll require &lt;code&gt;Ctrl+D&lt;/code&gt; (bookmark). They won't ask for API keys. They'll ask for optional, revocable File System Access permissions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar&lt;/a&gt; is a proof of concept: 264 professional-grade utilities, zero backend infrastructure, zero user data collection. If we can replace ImageMagick, FFmpeg, and PDFtk with browser-based implementations, what else can we move off the server?&lt;/p&gt;

&lt;p&gt;Your build pipeline? Your design tools? Your IDE?&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;## Try It&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Your laptop is already a supercomputer. Stop renting inferior clouds to do what your device handles in milliseconds.&lt;/p&gt;

&lt;p&gt;No registration required. No data uploaded. Just your browser's untapped potential.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;**&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;kreotar.com&lt;/a&gt;&lt;/em&gt;*&lt;/p&gt;

&lt;h2&gt;
  
  
  **
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What's your experience with WebAssembly in production? Drop a comment if you've replaced CLI tools with browser-based alternatives.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>I Processed 50M Files Without Installing a Single NPM Package (Or Renting a Server)</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Sun, 12 Apr 2026 13:28:07 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/i-processed-50m-files-without-installing-a-single-npm-package-or-renting-a-server-lj5</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/i-processed-50m-files-without-installing-a-single-npm-package-or-renting-a-server-lj5</guid>
      <description>&lt;p&gt;You know the drill. You need to merge three PDFs for a client deliverable. Five minutes later, you're npm install-ing a 400MB Puppeteer dependency, fighting Chromium headless mode on your M2 Mac, and watching node_modules crawl across your SSD like digital mold.&lt;br&gt;
By the time the script runs, you've pulled 847 sub-dependencies, three of which have critical security vulnerabilities according to npm audit. For a task that should take 0.4 seconds.&lt;br&gt;
We've accepted this as "modern development." We trade disk space, security surface area, and coffee-break waiting times for the privilege of not installing Adobe Acrobat.&lt;br&gt;
But what if the best runtime for utility scripts isn't Node.js, Python, or Docker? What if it's the browser tab you already have open?&lt;br&gt;
&lt;strong&gt;The Architecture of Impatience&lt;br&gt;
*&lt;em&gt;Last year, I watched a developer compress a batch of images for a web deployment. The workflow:&lt;br&gt;
Upload 47 images to a "free" SaaS tool&lt;br&gt;
Wait for Wi-Fi to push 800MB to Virginia&lt;br&gt;
Download the compressed versions&lt;br&gt;
Realize the metadata wasn't preserved&lt;br&gt;
Repeat&lt;br&gt;
Total time: 18 minutes. Total dependencies installed: 0. Total control over the process: also 0.&lt;br&gt;
This is the hidden tax of cloud-native convenience. We offloaded computation to servers we don't control, introduced network latency into operations that should be instantaneous, and called it "serverless."&lt;br&gt;
Real serverless is running FFmpeg in your browser without a Docker container.&lt;br&gt;
**WebAssembly Isn't the Future—It's the Patch&lt;br&gt;
**WebAssembly (WASM) arrived promising "near-native performance in the browser." Most developers heard "video games in Chrome" and tuned out. They missed the infrastructure revolution.&lt;br&gt;
When you compile C++, Rust, or Go to WASM, you're not creating a web app. You're creating a binary that executes in a sandboxed, zero-trust environment using the host machine's bare metal. Your browser becomes a secure runtime that can:&lt;br&gt;
Execute video encoding (FFmpeg.wasm) faster than Electron apps&lt;br&gt;
Process PDFs using C++ libraries compiled for the web&lt;br&gt;
Manipulate images via WebGL-accelerated filters&lt;br&gt;
Run regex engines and JSON parsers at native speeds&lt;br&gt;
All without node_modules. All without docker run. All without sending your client's confidential files to a server in another jurisdiction.&lt;br&gt;
The 50M File Experiment&lt;br&gt;
We built Kreotar to test a hypothesis: Could we replace 264 common developer and productivity utilities with browser-native implementations? Not wrappers around API calls—actual client-side processing.&lt;br&gt;
The stack:&lt;br&gt;
WebAssembly for compute-intensive operations (PDF rendering, image compression, video codecs)&lt;br&gt;
Web Workers for parallel processing (multi-threading without blocking the UI)&lt;br&gt;
IndexedDB for temporary local storage (offline capability)&lt;br&gt;
Service Workers for instant loading and offline functionality&lt;br&gt;
The result? 50 million files processed. Zero servers rented. Zero data breaches possible (nothing to breach).&lt;br&gt;
Here's what that means practically:&lt;br&gt;
The JSON Formatter That Works Offline&lt;br&gt;
You paste a malformed 10MB API response. Traditional web tools? Upload, parse server-side, return formatted JSON. Kreotar? WebAssembly-based parser runs locally in 12ms. Works on airplane Wi-Fi that barely loads Slack.&lt;br&gt;
Regex Testing Without RegExr.com&lt;br&gt;
Your pattern matches against sensitive log files. Instead of pasting production data into a third-party website (and hoping their analytics don't capture it), the regex engine executes in a sandboxed Worker. Your logs never leave the tab.&lt;br&gt;
Video Conversion on a Chromebook&lt;br&gt;
Need to strip audio from an MP4 for a presentation? FFmpeg.wasm runs in the browser using the device's GPU acceleration. A $300 Chromebook processes video faster than a 2019 MacBook Pro running cloud-based conversion tools.&lt;br&gt;
Why Your Browser Is the Better Runtime&lt;br&gt;
Node.js revolutionized development by bringing JavaScript to the server. But for utility scripts—file conversion, format validation, image manipulation—we went backwards. We installed gigabytes of dependencies to do tasks the browser could handle natively.&lt;br&gt;
Consider the security model:&lt;br&gt;
CLI Tool Approach:&lt;br&gt;
Installs 300 dependencies&lt;br&gt;
Requires filesystem access&lt;br&gt;
Runs with user-level permissions&lt;br&gt;
Updates break workflows&lt;br&gt;
Browser-WASM Approach:&lt;br&gt;
Zero installation&lt;br&gt;
Sandboxed execution (no filesystem access beyond Downloads)&lt;br&gt;
Runs in isolated origin context&lt;br&gt;
Updates are cache-busted Service Worker refreshes&lt;br&gt;
Or the performance model:&lt;br&gt;
**Docker Approach:&lt;br&gt;
**Spin up container (2-5 seconds)&lt;br&gt;
Mount volumes&lt;br&gt;
Process file&lt;br&gt;
Clean up container&lt;br&gt;
**Browser Approach:&lt;br&gt;
**Drag file into tab&lt;br&gt;
WebAssembly processes using local GPU (0.4 seconds)&lt;br&gt;
Download result&lt;br&gt;
The browser isn't just a document viewer anymore. It's a secure, sandboxed, hardware-accelerated runtime environment that 4.9 billion people already have installed.&lt;br&gt;
**The Developer Workflow Shift&lt;br&gt;
**This changes how we think about "tools." Instead of maintaining a graveyard of Homebrew packages—each with conflicting dependencies and deprecation notices—you maintain a bookmarks folder of utilities that:&lt;br&gt;
Never need updates (the browser handles versioning)&lt;br&gt;
Work identically on macOS, Linux, Windows, and iPad&lt;br&gt;
Process files using your local hardware (faster than cloud for most operations)&lt;br&gt;
Respect your privacy by architectural necessity&lt;br&gt;
For DevOps engineers, this means format conversion without spinning up Lambda functions. For frontend developers, it means image optimization without configuring Sharp. For security teams, it means file analysis without trusting third-party services with proprietary data.&lt;br&gt;
**The Privacy-Performance Convergence&lt;br&gt;
**We've been told to choose between privacy and convenience. Either use slow local tools or fast cloud services that monetize your data.&lt;br&gt;
WebAssembly breaks this false dichotomy. Kreotar processes files faster than cloud alternatives because there's no network hop. The privacy isn't a feature—it's a side effect of the architecture. When computation happens in your browser's WASM sandbox, there is no server to subpoena, no database to leak, no employee to misuse access.&lt;br&gt;
50 million files processed without a single upload isn't a marketing claim. It's a technical impossibility for traditional SaaS architecture.&lt;br&gt;
**The Future Is Client-Heavy&lt;br&gt;
**We're witnessing the pendulum swing back from cloud-centralized computing to edge-heavy processing. Not because developers miss the complexity of local installations, but because browsers have matured into legitimate runtime environments.&lt;br&gt;
The next generation of developer tools won't require npm install. They'll require Ctrl+D (bookmark). They won't ask for API keys. They'll ask for File System Access API permissions (optional, revocable).&lt;br&gt;
*&lt;/em&gt;&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar&lt;/a&gt;&lt;/strong&gt; is a proof of concept: 264 professional-grade utilities, zero backend infrastructure, zero user data collection. If we can replace ImageMagick, FFmpeg, and PDFtk with browser-based implementations, what else can we move off the server?&lt;br&gt;
Your build pipeline? Your design tools? Your IDE?&lt;br&gt;
The browser is already a supercomputer. Stop renting inferior clouds to do what your laptop can handle in milliseconds.&lt;br&gt;
Try the zero-install toolkit: &lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;kreotar.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Client-Side vs Server-Side: When Is Browser Processing Actually Better?</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Sat, 11 Apr 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/client-side-vs-server-side-when-is-browser-processing-actually-better-1ep4</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/client-side-vs-server-side-when-is-browser-processing-actually-better-1ep4</guid>
      <description>&lt;p&gt;I built a company on client-side processing. I still server-side process half my traffic. Here's the honest breakdown.&lt;br&gt;
**When Browser Processing Wins&lt;br&gt;
**Privacy-first workflows. Medical images, legal docs, personal photos. If leaking the data destroys trust, process locally.&lt;br&gt;
Offline or spotty connectivity. My tools work in airplanes, basements, rural Kenya. Serverless literally means "no server to fail."&lt;br&gt;
Instant feedback loops. Image filters, text analysis, code formatters. Sub-100ms response times beat any API roundtrip.&lt;br&gt;
When Server-Side Wins (And I Admit It)&lt;br&gt;
Batch processing. Converting 500 images? Your laptop will thermal throttle. My server farm won't.&lt;br&gt;
Proprietary models. I can't ship GPT-4 or Stable Diffusion in a 200KB WASM chunk. Yet.&lt;br&gt;
Persistent storage. Browsers clear IndexedDB "when they feel like it." My databases don't.&lt;br&gt;
The Hybrid Truth&lt;br&gt;
&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar's&lt;/a&gt; real architecture isn't client-side OR server-side. It's client-side-first with server-side-escape-hatches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User uploads file
    ↓
&amp;lt; 50MB? → WASM processing locally
&amp;gt; 50MB? → Encrypted stream to server, process, return, forget
    ↓
Result delivered
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server doesn't store. It processes and amnesia. But it exists because some jobs are too heavy for a phone.&lt;br&gt;
Where Browser Processing Surprises You&lt;br&gt;
&lt;strong&gt;Cold starts:&lt;/strong&gt; A WASM module loads faster than a Lambda function warms up. First user pays the cost, not every user.&lt;br&gt;
Parallelism: 8 Web Workers on an M3 MacBook beat a single server core for embarrassingly parallel tasks (image resizing, format conversion).&lt;br&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; My browser users cost me $0 in compute. My server users cost me $0.002 per job. At scale, that matters.&lt;br&gt;
One Thing I'd Do Differently&lt;br&gt;
I spent months trying to make video processing work in browser. It doesn't, reliably, for files &amp;gt;100MB. I should have built the server fallback earlier instead of pretending WASM would magically improve.&lt;br&gt;
**Question ?&lt;br&gt;
**Is "process in browser" becoming a UX pattern users actually recognize and trust? Or are we still educating? I see "no upload required" badges converting 40% better than "fast processing." But I can't tell if users understand why that's good.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Process PDFs in Browser Without Uploading: A Practical Guide</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Sat, 11 Apr 2026 12:00:00 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/process-pdfs-in-browser-without-uploading-a-practical-guide-36gc</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/process-pdfs-in-browser-without-uploading-a-practical-guide-36gc</guid>
      <description>&lt;p&gt;I built this because I watched a lawyer upload a client's contract to a "free PDF tool" with a .ru domain. Never again.&lt;br&gt;
&lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;What We're Building&lt;br&gt;
&lt;/a&gt;A browser-based PDF processor that extracts text, merges pages, and adds watermarks. Zero server roundtrips. The PDF never leaves the machine.&lt;br&gt;
&lt;strong&gt;Step 1: The Library&lt;/strong&gt;&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;// pdf-lib handles manipulation, pdfjs-dist handles extraction&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pdfjs&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;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// pdfjs needs its worker loaded manually in most bundlers&lt;/span&gt;
&lt;span class="nx"&gt;pdfjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://cdnjs.cloudflare.com/ajax/libs/pdf.js/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pdfjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/pdf.worker.min.js`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Read Without Uploading&lt;/strong&gt;&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processLocalPDF&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// File stays in browser memory only&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdf&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;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Extract text from page 1&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfJsDoc&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;pdfjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&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="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&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;page&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;pdfJsDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;textContent&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTextContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;Step 3: Modify and Download&lt;br&gt;
*&lt;/em&gt;&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;watermarkAndSave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;watermarkText&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;pdf&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;pdfBytes&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="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Add watermark to each page&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;page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watermarkText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;g&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modified&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;pdf&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="c1"&gt;// Trigger download, no server involved&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&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;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;modified&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;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="nx"&gt;blob&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;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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processed.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;p&gt;*&lt;em&gt;The Gotcha *&lt;/em&gt;&lt;br&gt;
PDFs with embedded fonts are 10x larger in memory than their file size. A 5MB PDF can balloon to 80MB when pdf-lib parses it. I cap processing at 50MB input files—above that, I warn users that their tab might crash.&lt;br&gt;
**One Thing I'd Do Differently&lt;br&gt;
**I initially tried to parse PDFs with regex. Don't. The spec is 800 pages of chaos. Use the libraries. They're battle-tested by Mozilla and maintained by people who've read the spec so you don't have to.&lt;br&gt;
**Question ?&lt;br&gt;
**Has anyone solved client-side PDF creation from scratch (not manipulation) at reasonable speeds? Generating a 100-page report from JSON data takes 4 seconds in my tests. Acceptable, but feels wrong.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Built 243 Browser Tools with WASM. Here's What Broke.</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Sat, 11 Apr 2026 07:12:05 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/i-built-243-browser-tools-with-wasm-heres-what-broke-1n6g</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/i-built-243-browser-tools-with-wasm-heres-what-broke-1n6g</guid>
      <description>&lt;p&gt;The first 50 tools worked great. Tool #51—a PDF form filler—crashed Safari because I assumed 2GB of WASM memory was standard. It's not. iOS kills tabs at 1.4GB.&lt;br&gt;
What I Got Wrong&lt;br&gt;
I architected &lt;a href="//kreotar.com"&gt;Kreotar&lt;/a&gt; around the belief that "client-side = unlimited scale." Wrong. Browsers are weird, capricious beasts. Each has its own memory ceiling, its own CORS paranoia, its own idea of what "background processing" means.&lt;br&gt;
The Architecture That Survived&lt;/p&gt;

&lt;p&gt;┌─────────────────┐&lt;br&gt;
│   UI Layer      │  (React, disposable)&lt;br&gt;
├─────────────────┤&lt;br&gt;
│   Worker Pool   │  (4-8 Web Workers, spawn/kill aggressively)&lt;br&gt;
├─────────────────┤&lt;br&gt;
│   WASM Bridge   │  (Emscripten, -sALLOW_MEMORY_GROWTH=1)&lt;br&gt;
├─────────────────┤&lt;br&gt;
│   Tool Modules  │  (Lazy-loaded, 200KB chunks max)&lt;br&gt;
└─────────────────┘&lt;br&gt;
The bridge layer was the surprise hero. I initially compiled each tool as a standalone WASM blob. Tool #87 (HEIC converter) was 14MB. First load: 8 seconds on 4G. Dead on arrival.&lt;br&gt;
Now tools stream. WASM compiles on first use, not page load. Users wait once per tool, not once per visit.&lt;br&gt;
&lt;strong&gt;What Actually Failed&lt;/strong&gt;&lt;br&gt;
FFmpeg in browser: 18MB WASM, 6-minute compile on mobile. Moved to server-side for videos &amp;gt;100MB. I admit defeat.&lt;br&gt;
SharedArrayBuffer: Required for true parallel processing. Still blocked by default in Firefox with certain extensions. Had to build a fallback that uses postMessage and feels 40% slower.&lt;br&gt;
File System Access API: Chrome-only. My "seamless local file editing" feature works for 60% of users. I ship it anyway with a graceful degradation to downloads.&lt;br&gt;
One Thing I'd Do Differently&lt;br&gt;
I spent three weeks optimizing WASM build flags before realizing the bottleneck was DOM thrashing. Profile your actual app, not your benchmarks.&lt;br&gt;
The Genuine Question&lt;br&gt;
Has anyone found a reliable way to detect "this browser tab is about to be killed by the OS" before it happens? I've tried memory pressure APIs, performance.memory, heartbeat workers. All garbage.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>buildinpublic</category>
      <category>react</category>
    </item>
    <item>
      <title>How to compress PDFs in the browser without uploading to servers: A complete guide</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Fri, 10 Apr 2026 18:30:00 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/how-to-compress-pdfs-in-the-browser-without-uploading-to-servers-a-complete-guide-51d6</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/how-to-compress-pdfs-in-the-browser-without-uploading-to-servers-a-complete-guide-51d6</guid>
      <description>&lt;p&gt;Your PDFs contain sensitive data. Tax forms, contracts, medical records. Yet most online tools want you to upload them to mysterious servers in who-knows-where.&lt;br&gt;
I built Kreotar's PDF compressor to solve this exact paranoia. Everything happens in your browser. Here's exactly how you can implement the same architecture.&lt;br&gt;
Step 1: The Architecture Decision&lt;br&gt;
We use PDF-lib (client-side JS) combined with custom WASM modules for image compression. The key is handling everything in a Web Worker so the UI stays responsive during heavy processing.&lt;br&gt;
JavaScript&lt;br&gt;
Copy&lt;br&gt;
// pdf-processor.worker.js&lt;br&gt;
import * as PDFLib from 'pdf-lib';&lt;br&gt;
import { PDFDocument } from 'pdf-lib';&lt;br&gt;
import { createImageCompressionWasm } from './wasm-image-compress';&lt;/p&gt;

&lt;p&gt;self.onmessage = async (event) =&amp;gt; {&lt;br&gt;
  const { fileBuffer, quality = 0.7 } = event.data;&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
    const pdfDoc = await PDFDocument.load(fileBuffer);&lt;br&gt;
    const pages = pdfDoc.getPages();&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let totalSaved = 0;

// Process each page
for (let i = 0; i &amp;lt; pages.length; i++) {
  const page = pages[i];

  // Extract images from page
  const images = await extractImagesFromPage(page);

  for (const image of images) {
    const originalSize = image.data.length;

    // Compress using WASM (mozjpeg compiled to WASM)
    const compressed = await createImageCompressionWasm({
      data: image.data,
      quality: quality * 100,
      format: 'jpeg'
    });

    totalSaved += (originalSize - compressed.length);

    // Replace image in PDF
    await replaceImageInPage(page, image.ref, compressed);
  }

  // Report progress
  self.postMessage({ 
    type: 'progress', 
    current: i + 1, 
    total: pages.length 
  });
}

const pdfBytes = await pdfDoc.save();

self.postMessage({ 
  type: 'complete', 
  result: pdfBytes,
  compressionRatio: totalSaved / fileBuffer.length
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;} catch (error) {&lt;br&gt;
    self.postMessage({ type: 'error', message: error.message });&lt;br&gt;
  }&lt;br&gt;
};&lt;br&gt;
Step 2: The React Integration&lt;br&gt;
Here's how to wire it up in your frontend:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PdfWorker&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-processor.worker?worker&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;PdfCompressor&lt;/span&gt; &lt;span class="o"&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;compressionStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCompressionStats&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;workerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;processPdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&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="nx"&gt;file&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="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setProgress&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="c1"&gt;// Initialize worker&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;PdfWorker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;workerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Read file as ArrayBuffer&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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="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;e&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;compressionRatio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&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="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setCompressionStats&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
              &lt;span class="na"&gt;originalSize&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;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;newSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&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="na"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;compressionRatio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;// Create download blob&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&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;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;result&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;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="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Auto-download&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;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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`compressed-&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;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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;reject&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&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;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;break&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="c1"&gt;// Start processing&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;fileBuffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="c1"&gt;// Compression quality&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Transfer ownership for performance&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdf-compressor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; 
        &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
        &lt;span class="nx"&gt;accept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;processPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;width&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;progress&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;compressed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;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;compressionStats&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stats&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Original&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;compressionStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalSize&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="nx"&gt;KB&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Saved&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;compressionStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;%&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;Step 3: Handling the Gotchas&lt;br&gt;
Memory limits: Browsers crash around 2GB of RAM usage. For large PDFs, I process pages in chunks:&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;// Handle large PDFs in chunks to avoid memory crashes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processInChunks&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="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;results&lt;/span&gt; &lt;span class="o"&gt;=&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;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;pages&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&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;chunk&lt;/span&gt; &lt;span class="o"&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;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Force garbage collection between chunks (hacky but necessary)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&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;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processPage&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;results&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;CORS issues: If your WASM module is on a CDN, ensure proper headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Privacy Win&lt;br&gt;
Your file never leaves your laptop. Check the Network tab in DevTools - zero uploads. That's the magic of client-side processing.&lt;br&gt;
I made the mistake early on of trying to use serverless functions for this. The latency killed the UX. Plus, who wants to upload their tax documents to a random Lambda function?&lt;br&gt;
Try it yourself: &lt;a href="https://kreotar.com/en/tools/pdf/compress" rel="noopener noreferrer"&gt;Kreotar PDF Compressor&lt;/a&gt;&lt;br&gt;
What other PDF operations are you trying to client-side? I might already have a tool for it in the sitemap above.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
    <item>
      <title>I built 47 developer tools running entirely in WebAssembly. Here's the architecture that made it possible.</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Fri, 10 Apr 2026 16:25:11 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/i-built-47-developer-tools-running-entirely-in-webassembly-heres-the-architecture-that-made-it-3lln</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/i-built-47-developer-tools-running-entirely-in-webassembly-heres-the-architecture-that-made-it-3lln</guid>
      <description>&lt;p&gt;I messed up the first iteration. Badly.&lt;br&gt;
When I started building &lt;a href="https://kreotar.com" rel="noopener noreferrer"&gt;Kreotar's&lt;/a&gt; developer toolkit (JSON formatter, Base64 encoder, hash generators - you know the drill), I thought "hey, let's just use standard JavaScript string manipulation." Fast forward to processing a 50MB JSON file, and I watched the main thread freeze like a deer in headlights. The browser tab became unresponsive for 8 seconds. Users were angry. I was embarrassed.&lt;br&gt;
That's when I went all-in on WebAssembly.&lt;br&gt;
The WASM Architecture Behind the Tools&lt;br&gt;
Instead of parsing heavy text operations in JS, I compiled Rust code to WASM for the heavy lifting. Here's the core architecture:&lt;br&gt;
rust&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[wasm_bindgen]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;format_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;JsValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Parse error: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;formatted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_string_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;JsValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Format error: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;'comlink&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wasmWorker&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="err"&gt;'.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;wasm&lt;/span&gt;&lt;span class="py"&gt;.worker&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;useWasmTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;processing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setProcessing&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;processData&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="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setProcessing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;wasmWorker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;wasmModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;wasmModule&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setProcessing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;processData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;processing&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;Why This Matters&lt;br&gt;
The Base64 encoder handles 100MB files in under 2 seconds. The JSON formatter validates nested structures without crashing the tab. The hash generators (SHA-256, MD5, etc.) process files locally - your data never touches my server.&lt;br&gt;
I learned the hard way that JSON.parse() has limits. WASM doesn't magically fix everything, but when combined with Web Workers, it creates a truly non-blocking experience.&lt;br&gt;
The "oops" moment: I initially tried to pass massive strings directly between JS and WASM. Memory copying killed performance. Switching to SharedArrayBuffer for large payloads changed everything. Here's the pattern that actually works:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processLargeFile&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="nx"&gt;fileBuffer&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;wasmMemory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
    &lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;shared&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;result&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;wasmModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wasmMemory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&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;What I'm building next: A client-side regex tester that can handle multi-line 10MB log files without lag. Because waiting 5 seconds for a regex match is unacceptable.&lt;br&gt;
What's your WASM use case? I'm curious if others are hitting these same JS performance walls.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Privacy Policy | Kreotar</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Fri, 10 Apr 2026 11:01:00 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/privacy-policy-kreotar-5a8c</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/privacy-policy-kreotar-5a8c</guid>
      <description>&lt;p&gt;Building trust starts with clarity. Kreotar's ultra-specific privacy policy gives developers and enterprises the transparency they need to innovate safely. Dive into the details and see the difference.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>productivity</category>
      <category>tech</category>
    </item>
    <item>
      <title>Currency Converter Online - Kreotar | Kreotar</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Fri, 10 Apr 2026 02:36:18 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/currency-converter-online-kreotar-kreotar-fh1</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/currency-converter-online-kreotar-kreotar-fh1</guid>
      <description>&lt;p&gt;Mastering currency conversion with Kreotar is all about precision and insight. Our platform delivers real-time data and advanced analytics, making it the go-to solution for businesses needing reliable financial tools. Dive into our technical guide to unlock its full potential.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>productivity</category>
      <category>tech</category>
    </item>
    <item>
      <title>The Ultimate Developer &amp; JSON Toolkit</title>
      <dc:creator>göktürk kahriman</dc:creator>
      <pubDate>Fri, 10 Apr 2026 01:01:47 +0000</pubDate>
      <link>https://dev.to/gktrk_kahriman_192cb6da/the-ultimate-developer-json-toolkit-3ldp</link>
      <guid>https://dev.to/gktrk_kahriman_192cb6da/the-ultimate-developer-json-toolkit-3ldp</guid>
      <description>&lt;p&gt;Implementing a connected workflow across converter, image, utility, pdf, and ai tools unlocks seamless integration. This ecosystem reduces friction and accelerates delivery.&lt;/p&gt;

</description>
      <category>tools</category>
      <category>productivity</category>
      <category>tech</category>
    </item>
  </channel>
</rss>
