<?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: WebsiteGeek</title>
    <description>The latest articles on DEV Community by WebsiteGeek (@websitegeek).</description>
    <link>https://dev.to/websitegeek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4012983%2F9565d154-1fb9-40b1-a829-25b4e4f91dea.png</url>
      <title>DEV Community: WebsiteGeek</title>
      <link>https://dev.to/websitegeek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/websitegeek"/>
    <language>en</language>
    <item>
      <title>Building a PDF Toolkit That Never Touches a Server</title>
      <dc:creator>WebsiteGeek</dc:creator>
      <pubDate>Thu, 27 Aug 2026 02:47:32 +0000</pubDate>
      <link>https://dev.to/websitegeek/building-a-pdf-toolkit-that-never-touches-a-server-288j</link>
      <guid>https://dev.to/websitegeek/building-a-pdf-toolkit-that-never-touches-a-server-288j</guid>
      <description>&lt;p&gt;Every PDF tool follows the same pattern: upload your file, wait, download the result. That server round-trip exists because PDF processing is genuinely expensive — parsing a file format never designed to be edited, re-encoding pages, running compression. Doing that in a browser tab used to mean either a thin wrapper around a server API, or nothing at all.&lt;/p&gt;

&lt;p&gt;That's changed. WebAssembly-compiled versions of real PDF libraries are fast enough now to run entirely client-side, and building DeskRamp meant figuring out what that actually takes inside a Chrome extension, not just a regular web page.&lt;/p&gt;

&lt;p&gt;The CSP wall&lt;/p&gt;

&lt;p&gt;Manifest V3 extensions get a strict default Content Security Policy, and WebAssembly needs an explicit exception to run at all:&lt;/p&gt;

&lt;p&gt;"content_security_policy": {&lt;br&gt;
  "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Without 'wasm-unsafe-eval' explicitly declared, WebAssembly.instantiate() throws immediately — Chrome treats compiled WASM the same security-sensitive way it treats eval(), since both execute code the browser can't statically analyze ahead of time. This is a one-line fix once you know it's needed, but it's the first thing that breaks silently if you're porting WASM-based processing into an extension for the first time.&lt;/p&gt;

&lt;p&gt;Picking a library per task, not one library for everything&lt;/p&gt;

&lt;p&gt;No single library covers merge, split, compress, convert, and e-Sign well. DeskRamp bundles a few, each doing the one thing it's actually good at:&lt;/p&gt;

&lt;p&gt;pdf.js — rendering and reading PDF structure&lt;br&gt;
pdf-lib — merging, splitting, and building new PDF documents&lt;br&gt;
qpdf (compiled to WASM) — the actual compression and password/decryption work, which needs low-level access to PDF's internal object structure that higher-level libraries don't expose&lt;br&gt;
docx / pptxgen / xlsx libraries — format conversion into and out of Office formats&lt;/p&gt;

&lt;p&gt;This means the extension ships several hundred KB to a few MB of vendored library code, which matters for install size and initial load time. The practical fix is lazy-loading: nothing pulls in qpdf's WASM binary until the user actually clicks "Compress," rather than loading every processing library up front for a toolkit most of a session won't touch.&lt;/p&gt;

&lt;p&gt;The real ceiling: browser tab memory, not CPU&lt;/p&gt;

&lt;p&gt;The unexpected constraint wasn't processing speed — modern WASM PDF processing is fast enough that a merge or split feels instant on anything but a huge file. It's memory. A browser tab has a much lower practical memory ceiling than a server process does, and a large multi-hundred-page PDF fully parsed into memory, plus the WASM runtime's own working memory, plus whatever else the tab is holding, can hit that ceiling in a way a server-side version of the same operation never would.&lt;/p&gt;

&lt;p&gt;The mitigation is processing in chunks where the library supports it (streaming page-by-page rather than materializing the whole document object graph at once) and being explicit about releasing references (ArrayBuffers, typed arrays) as soon as a stage finishes, rather than trusting garbage collection to catch up on its own timeline while a multi-step operation is still running.&lt;/p&gt;

&lt;p&gt;Why this is worth the extra complexity&lt;/p&gt;

&lt;p&gt;The upload step in a typical PDF tool isn't just slower — it's the whole reason people hesitate to use these tools for anything they'd actually rather not hand to a server: contracts, IDs, financial documents. Once WASM-based processing is fast enough to feel native, there's no real reason left to default to a server round-trip for tasks like merge, split, compress, and convert. The extra engineering constraints (CSP, bundle size, memory ceilings) are the actual cost of that tradeoff — worth paying once, not something that needs solving per-feature.&lt;/p&gt;

&lt;p&gt;If you're curious what this looks like shipped: &lt;a href="https://chromewebstore.google.com/detail/deskramp-pdf-toolkit-shee/fahhfkcilhjedkdcbeekfipjbmgnmnbk" rel="noopener noreferrer"&gt;DeskRamp&lt;/a&gt; is free on the Chrome Web Store.&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>javascript</category>
      <category>extensions</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Why I Stopped Trying to Block Facebook Entirely</title>
      <dc:creator>WebsiteGeek</dc:creator>
      <pubDate>Wed, 26 Aug 2026 02:30:51 +0000</pubDate>
      <link>https://dev.to/websitegeek/why-i-stopped-trying-to-block-facebook-entirely-5a6a</link>
      <guid>https://dev.to/websitegeek/why-i-stopped-trying-to-block-facebook-entirely-5a6a</guid>
      <description>&lt;p&gt;I've tried almost every site blocker that exists. StayFocusd, Cold Turkey, Freedom, BlockSite — I've installed and uninstalled all of them at some point over the years, usually within a week of a New Year's resolution to "focus more." They all work the same way: you give them a list of sites, and they block those sites. Simple, effective, and completely wrong for how I actually use the internet.&lt;/p&gt;

&lt;p&gt;Here's the problem nobody talks about: the sites I need to block aren't sites I never need. Facebook has a Marketplace listing I'm mid-conversation on. Instagram has a client's DM I'm waiting on. X has a thread I actually need to reference for work. Blocking the whole domain doesn't just remove the distraction — it removes the one legitimate reason I had the tab open in the first place.&lt;/p&gt;

&lt;p&gt;So I'd disable the blocker "just for five minutes," and you already know how that story ends.&lt;/p&gt;

&lt;p&gt;The distraction isn't the site. It's the feed.&lt;/p&gt;

&lt;p&gt;At some point I realized the thing pulling me back in was never the platform — it was always the feed. The infinite, algorithmically-tuned scroll that exists specifically to keep me there past the point I intended. The Marketplace listing, the DM, the work thread — none of that is what hijacks twenty minutes of my afternoon. The feed is.&lt;/p&gt;

&lt;p&gt;That reframing changed what I was actually trying to build. Not a better block list. A tool that understands the difference between a site and the specific part of that site engineered to be addictive.&lt;/p&gt;

&lt;p&gt;What I ended up building&lt;/p&gt;

&lt;p&gt;FocusRamp is a Chrome extension that hides distracting feeds — Facebook's, Instagram's, X's — instead of blocking the whole site. Open Facebook and the news feed simply isn't there, but Marketplace, Groups, and Messenger still work exactly as they always have. You keep what you need. You lose what you don't.&lt;/p&gt;

&lt;p&gt;On top of that, it does the things I actually wanted from a blocker in the first place:&lt;/p&gt;

&lt;p&gt;Site blocking for the genuinely useless stuff — the sites where there's no "but I need this part" exception, so a full block makes sense.&lt;br&gt;
A real Pomodoro timer, because focus mode without a session structure is just a passive setting I forget is even on.&lt;br&gt;
Scheduled focus mode, so it turns itself on during work hours instead of relying on me to remember every morning.&lt;br&gt;
Why I made it free&lt;/p&gt;

&lt;p&gt;I didn't want to build something I wouldn't trust installing myself, and a tool that asks for payment before you've even tried it is a tool I usually never install at all. So the core of FocusRamp — site blocking, feed hiding, the Pomodoro timer, scheduling — is free forever, no account required. There's a Pro version with some extra features, but it's a one-time purchase, not a subscription. I have enough subscriptions I've forgotten I'm paying for; I didn't want to add to anyone else's pile.&lt;/p&gt;

&lt;p&gt;If you've been through the same cycle — install a blocker, disable it a week later, feel vaguely guilty, repeat — I built this for exactly that problem.&lt;/p&gt;

&lt;p&gt;FocusRamp is free on the Chrome Web Store: &lt;a href="https://chromewebstore.google.com/detail/focusramp-site-blocker-hi/pjeddidcdnhkgioednidaafafgnblikm" rel="noopener noreferrer"&gt;chromewebstore.google.com/detail/focusramp-site-blocker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or read more about it here: &lt;a href="https://websitegeek.net/focusramp/" rel="noopener noreferrer"&gt;websitegeek.net/focusramp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd genuinely like to hear what's missing if you try it — this is still early, and the roadmap is shaped by what people actually hit friction on, not a feature list I decided on alone.&lt;/p&gt;

</description>
      <category>socialmedia</category>
      <category>extensions</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to check your browser's privacy with 30 lines of JavaScript — and what I learned</title>
      <dc:creator>WebsiteGeek</dc:creator>
      <pubDate>Wed, 08 Jul 2026 16:15:00 +0000</pubDate>
      <link>https://dev.to/websitegeek/how-to-check-your-browsers-privacy-with-30-lines-of-javascript-and-what-i-learned-39o6</link>
      <guid>https://dev.to/websitegeek/how-to-check-your-browsers-privacy-with-30-lines-of-javascript-and-what-i-learned-39o6</guid>
      <description>&lt;h2&gt;
  
  
  What most people don't know about their browser
&lt;/h2&gt;

&lt;p&gt;Your browser leaks more than you think — and you don't need a third-party tool to check it. Most of the signals you need are already in the browser's native APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6 checks I run (and the JS code)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. HTTPS status&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;window.location.protocol === 'https:'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Cookies enabled&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;navigator.cookieEnabled&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Local storage available&lt;/strong&gt;&lt;br&gt;
Try/catch a localStorage.setItem() call&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Do Not Track signal&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;navigator.doNotTrack === '1'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Private/Incognito heuristic&lt;/strong&gt;&lt;br&gt;
Check localStorage quota — private mode caps it at ~5MB in most browsers&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Browser detection&lt;/strong&gt;&lt;br&gt;
navigator.userAgent parsing for Chrome/Firefox/Safari/Edge&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning it into a score
&lt;/h2&gt;

&lt;p&gt;Each check that passes adds points to a score out of 100. I weight DNT and HTTPS more heavily because they have the most real-world impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built with this
&lt;/h2&gt;

&lt;p&gt;A free browser privacy checker that runs these 6 checks instantly and gives you a score, a verdict, and a personalized list of things to fix.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://websitegeek.net/privacy-security-checkup/" rel="noopener noreferrer"&gt;Browser Privacy &amp;amp; Security Checkup&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I want to add next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;WebRTC leak detection (navigator.mediaDevices.enumerateDevices)&lt;/li&gt;
&lt;li&gt;Canvas fingerprint score&lt;/li&gt;
&lt;li&gt;Third-party cookie isolation check&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What would you add? Have you built something similar?&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>security</category>
      <category>privacy</category>
    </item>
    <item>
      <title>I built a WiFi QR Code Generator that runs 100% in your browser</title>
      <dc:creator>WebsiteGeek</dc:creator>
      <pubDate>Fri, 03 Jul 2026 16:20:00 +0000</pubDate>
      <link>https://dev.to/websitegeek/i-built-a-wifi-qr-code-generator-that-runs-100-in-your-browser-8ld</link>
      <guid>https://dev.to/websitegeek/i-built-a-wifi-qr-code-generator-that-runs-100-in-your-browser-8ld</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Sharing your WiFi password out loud is awkward. Typing it on a guest's phone takes forever. And some passwords are 20+ characters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The WiFi QR Code Trick
&lt;/h2&gt;

&lt;p&gt;A WiFi QR code encodes your network credentials in a format phones understand natively — iOS 11+ and Android 10+ both support it without any app. Guest scans it, connects instantly.&lt;/p&gt;

&lt;p&gt;The QR string format looks like this:&lt;br&gt;
WIFI:T:WPA;S:YourNetworkName;P:YourPassword;;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It (in 40 lines of JavaScript)
&lt;/h2&gt;

&lt;p&gt;[paste 20–30 lines of your actual JS generator code here]&lt;/p&gt;

&lt;p&gt;The key is using a QR library like qrcode.js and building the WIFI: string dynamically from the form inputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Added for Better UX
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Show/hide password toggle&lt;/li&gt;
&lt;li&gt;Network type dropdown (WPA2, WPA3, WEP, None)&lt;/li&gt;
&lt;li&gt;Hidden network checkbox&lt;/li&gt;
&lt;li&gt;One-click download as PNG&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;p&gt;I designed this with 4 scenarios in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;House guests who can't connect&lt;/li&gt;
&lt;li&gt;Airbnb hosts who change passwords monthly&lt;/li&gt;
&lt;li&gt;Office WiFi for visitor access&lt;/li&gt;
&lt;li&gt;New home setup — print it and stick it on the router&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Free
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://websitegeek.net/wifi-qr-code-generator/" rel="noopener noreferrer"&gt;WiFi QR Code Generator&lt;/a&gt; — no signup, nothing stored.&lt;/p&gt;

&lt;p&gt;I also built a &lt;a href="https://websitegeek.net/password-generator-strength-checker/" rel="noopener noreferrer"&gt;Password Generator&lt;/a&gt; and a &lt;a href="https://websitegeek.net/privacy-security-checkup/" rel="noopener noreferrer"&gt;Browser Privacy Checker&lt;/a&gt; as part of the same toolkit.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
