<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Honest Toolbox]]></title><description><![CDATA[The Honest Toolbox]]></description><link>https://easy28.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 08:03:02 GMT</lastBuildDate><atom:link href="https://easy28.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why Your Laser Cutter Keeps Dropping Text — Convert SVG Text to Paths]]></title><description><![CDATA[You send a perfectly good SVG to the laser cutter and the job comes back with every label missing. Preview on your screen: fine. On the machine's software…]]></description><link>https://easy28.hashnode.dev/why-your-laser-cutter-keeps-dropping-text-convert-svg-text-to-paths</link><guid isPermaLink="true">https://easy28.hashnode.dev/why-your-laser-cutter-keeps-dropping-text-convert-svg-text-to-paths</guid><category><![CDATA[SVG]]></category><category><![CDATA[webdev]]></category><category><![CDATA[laser]]></category><dc:creator><![CDATA[easy28]]></dc:creator><pubDate>Mon, 02 Nov 2026 00:55:41 GMT</pubDate><content:encoded><![CDATA[<p>You send a perfectly good SVG to the laser cutter and the job comes back with every label missing. Preview on your screen: fine. On the machine's software: fine. Cut result: text gone.</p>
<p>The usual diagnosis is "the machine hates my font", and the usual fix people reach for is installing that font on every machine that touches the file. That works until it doesn't — until a collaborator, a print shop, or a firmware update gets in the loop. The robust fix is older than laser cutters themselves: stop shipping text, ship outlines.</p>
<h2>Why text is a liability inside an SVG</h2>
<p>An SVG <code>&lt;text&gt;</code> element doesn't contain letterforms. It contains a <em>reference</em> to letterforms: the font family name, a size, and a string. Rendering is delegated to whatever font engine opens the file. That's fine on the web, where you can ship a webfont alongside. It's a liability everywhere else:</p>
<ul>
<li>The receiving machine doesn't have the font, so it substitutes one — different metrics, different look, sometimes catastrophic reflow.</li>
<li>The machine <em>does</em> have the font, but a different version with different glyph outlines.</li>
<li>The software rasterizes or traces text with its own rules, and single-line engraving fonts get filled-font treatment.</li>
<li>Licensing: some font licenses don't permit embedding or conversion at all.</li>
</ul>
<p>A path, by contrast, is geometry. <code>M 10 10 L 20 10 L 20 20 Z</code> renders identically on a 2008 vinyl cutter and a 2026 fiber laser. Converting text to paths — outlines, in print terminology — removes the font from the equation entirely.</p>
<h2>When to convert (and when not to)</h2>
<p>Convert to paths when the file leaves your machine for machines, cutters, plotters, engravers, print shops, or when it will be embedded in something long-lived (a logo, a hardware panel legend, a product engraving spec).</p>
<p>Keep live text when the words must stay selectable, searchable, or translatable — documentation, wireframes, anything a screen reader should read. Outlined text is dead text: no copy-paste, no find, no accessibility tree.</p>
<p>One practical note: convert <em>last</em>. Do all your editing and sizing while the text is still text, because once it's outlines, fixing a typo means re-typing the word and converting again.</p>
<h2>How the conversion has to work</h2>
<p>A naive approach renders each string as one blob. Real text-to-path conversion has to work per glyph, because that's how SVG text actually lays out:</p>
<ul>
<li><strong>Per-glyph placement, not per-string.</strong> Each letter becomes its own path, positioned exactly where the text engine would have put it — including kerning pairs. A blob loses the ability to re-cut a single letter or reorder anything.</li>
<li><strong>Real text metrics.</strong> The converter has to understand font units and em squares, and emit coordinates in units that survive any <code>viewBox</code> — otherwise the outlines land at the wrong scale and the whole job cuts small.</li>
<li><strong>Everything visual survives the swap.</strong> Fill, stroke, opacity, transforms on the text element — the outlined version should be pixel-identical to the text version when rendered side by side.</li>
<li><strong>Honest failure modes.</strong> A converter should tell you when it can't map a glyph instead of silently dropping it. A missing <code>Z</code> on a contour or a literal <code>NaN</code> in path data means a gap in the cut line or a toolpath error on the machine.</li>
</ul>
<h2>The one-command version</h2>
<p><a href="https://svgeditor.dev/">SVG Editor</a> has a built-in Text to Path tool that does this conversion entirely in your browser: load the SVG, select the text, convert, download. Nothing is uploaded, nothing needs an account, and you get a side-by-side preview so you can verify the outlines match before you send the file anywhere. The full reasoning — the pain points, the per-glyph mechanics, the failure modes — is written up in <a href="https://svgeditor.dev/blog/svg-text-to-path">SVG text to path: why and how</a>.</p>
<h2>After the conversion: the machine-ready pipeline</h2>
<p>Once your text is outlined, the file is geometry-only and safe to hand to anything. The usual next steps for cutter workflows:</p>
<ol>
<li><strong>Clean up the geometry.</strong> <a href="https://svgeditor.dev/svg-optimizer">Optimize the SVG</a> — redundant nodes and editor metadata confuse CAM software more often than people expect.</li>
<li><strong>Export to a machine format.</strong> Convert directly to <a href="https://svgeditor.dev/svg-to-gcode">G-code</a> for CNC and diode lasers, or <a href="https://svgeditor.dev/svg-to-dxf">DXF</a> for laser cutters and plasma tables that expect CAD interchange. Both expect outlines — never live text.</li>
<li><strong>Preview before you cut.</strong> A quick render pass in the <a href="https://svgeditor.dev/svg-preview">SVG preview tool</a> catches an empty result before you find out with a wasted sheet of material.</li>
</ol>
<p>The pattern is always the same: text is for humans and editors, paths are for machines. Convert once, at the end, and the file becomes portable in a way no font installation scheme can match.</p>
]]></content:encoded></item><item><title><![CDATA[Your Favicon Vanishes in Dark Browser Themes — Fix It With One SVG]]></title><description><![CDATA[One SVG with a prefers-color-scheme media query flips your favicon for dark browser themes. Code, fallbacks, and the iOS transparency trap.]]></description><link>https://easy28.hashnode.dev/your-favicon-vanishes-in-dark-browser-themes-fix-it-with-one-svg</link><guid isPermaLink="true">https://easy28.hashnode.dev/your-favicon-vanishes-in-dark-browser-themes-fix-it-with-one-svg</guid><category><![CDATA[webdev]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[SVG]]></category><category><![CDATA[CSS3]]></category><dc:creator><![CDATA[easy28]]></dc:creator><pubDate>Sun, 01 Nov 2026 10:28:17 GMT</pubDate><content:encoded><![CDATA[<p>Your logo looks sharp on a light tab strip. Flip the OS to dark mode and the same icon sinks into the browser chrome — dark logo on dark background, invisible. Same site, same file, zero changes on your side.</p>
<p>This isn't something you can patch with a PNG swap. There is no <code>rel="icon-dark"</code> in HTML. But there <em>is</em> a clean fix, and it fits in a single file.</p>
<h2>The trick: CSS inside your SVG favicon</h2>
<p>SVG favicons can embed a <code>&lt;style&gt;</code> block, and browsers apply it — including media queries. That means one <code>favicon.svg</code> can restyle itself based on <code>prefers-color-scheme</code>:</p>
<pre><code class="language-svg">&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"&gt;
  &lt;style&gt;
    @media (prefers-color-scheme: dark) {
      .bg { fill: #101418; }
      .logo { fill: #f4f6f8; }
    }
    .bg { fill: #ffffff; }
    .logo { fill: #101418; }
  &lt;/style&gt;
  &lt;rect class="bg" width="32" height="32" rx="6"/&gt;
  &lt;path class="logo" d="..."/&gt;
&lt;/svg&gt;
</code></pre>
<p>The browser resolves the media query against the user's OS or browser theme at render time. Light theme → light background, dark strokes. Dark theme → dark background, light strokes. No JavaScript, no user-agent sniffing, no second file to keep in sync.</p>
<h2>Wire it up with a proper fallback</h2>
<p>Declare the SVG first, the classic <code>.ico</code> second:</p>
<pre><code class="language-html">&lt;link rel="icon" href="/favicon.svg" type="image/svg+xml"&gt;
&lt;link rel="icon" href="/favicon.ico" sizes="32x32"&gt;
&lt;link rel="apple-touch-icon" href="/apple-touch-icon.png"&gt;
</code></pre>
<p>Browsers that understand SVG favicons pick the first; everything else falls back to the <code>.ico</code>. Keep that fallback — as of 2026 every evergreen browser supports SVG icons, but crawlers, feed readers and some embedded webviews still grab <code>.ico</code>.</p>
<h2>Two dark-mode details people miss</h2>
<p><strong>1. The tab strip color is separate from your icon.</strong> The <code>theme-color</code> meta tag controls the browser UI tint:</p>
<pre><code class="language-html">&lt;meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)"&gt;
&lt;meta name="theme-color" content="#0b0d10" media="(prefers-color-scheme: dark)"&gt;
</code></pre>
<p>If your icon now flips but the toolbar stays light, this is the missing half.</p>
<p><strong>2. Safari on iOS flattens transparency onto black.</strong> Your crisp transparent PNG <code>apple-touch-icon</code> becomes a dark blob on a dark-mode home screen. Bake your brand background into the export. Safari's macOS pinned-tab icon is its own rabbit hole (mask-icon and its support matrix) — <a href="https://favicon.one/blog/dark-mode-favicon-safari/">worth a dedicated read</a>.</p>
<h2>A few honest limitations</h2>
<ul>
<li><code>prefers-color-scheme</code> inside SVG icons works in Chrome, Edge, Firefox and Safari for <code>favicon.svg</code>. Some third-party clients render the SVG without applying the media query — they show your light-theme default. That's exactly why the default styles should be readable on their own.</li>
<li>If your brand mark is genuinely mid-gray, it may be <em>adequate</em> in both themes. Adaptive icons matter most for black, white, or low-contrast marks.</li>
<li>Don't ship a 40 KB styled SVG where a 1 KB one works. Gradients and filters bloat what should be the smallest asset on your page.</li>
</ul>
<h2>The 30-second audit</h2>
<p>Want to know what your site currently serves in each theme?</p>
<ol>
<li>Open your site, toggle OS dark mode, look at the tab strip. If the icon vanishes, you're shipping a non-adaptive asset.</li>
<li>Check what your <code>&lt;link rel="icon"&gt;</code> tags actually resolve to with the <a href="https://favicon.one/favicon-checker/">Favicon Checker</a> — it reads the live page and reports every icon you declare.</li>
<li>If you're starting from a logo image or a brand name instead of an SVG, the <a href="https://favicon.one/svg-favicon-generator/">SVG favicon generator</a> and the <a href="https://favicon.one/text-to-favicon/">text-to-favicon tool</a> export a clean <code>favicon.svg</code> you can paste the <code>&lt;style&gt;</code> block into — free, runs entirely in your browser.</li>
</ol>
<p>Full walkthrough with the complete SVG template and the JS fallback for legacy browsers: <a href="https://favicon.one/blog/favicon-dark-mode/">Favicon Dark Mode: Adaptive Icons for Light &amp; Dark</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Your browser can run real jq now — no terminal, no uploads, no WSL]]></title><description><![CDATA[Every developer knows the moment: an API returns 4,000 lines of nested JSON, and you need to answer one question — which objects have status: "failed" and…]]></description><link>https://easy28.hashnode.dev/your-browser-can-run-real-jq-now-no-terminal-no-uploads-no-wsl</link><guid isPermaLink="true">https://easy28.hashnode.dev/your-browser-can-run-real-jq-now-no-terminal-no-uploads-no-wsl</guid><category><![CDATA[jq]]></category><category><![CDATA[json]]></category><category><![CDATA[webdev]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[easy28]]></dc:creator><pubDate>Sun, 01 Nov 2026 02:10:17 GMT</pubDate><content:encoded><![CDATA[<p>Every developer knows the moment: an API returns 4,000 lines of nested JSON, and you need to answer one question — <em>which objects have <code>status: "failed"</code> and what were their error codes?</em></p>
<p>If you have jq installed, it's a 10-second one-liner. If you don't — Windows admin box without admin rights, a locked-down corporate laptop, a client's machine you can't install anything on — your options have traditionally been: copy the JSON into a random web playground (and hope it doesn't store it), or squint at it in an editor.</p>
<p>There's a third option now: <strong>the actual jq binary, compiled to WebAssembly, running entirely inside your browser tab.</strong></p>
<h2>Same filter language, no installation</h2>
<p>We shipped real jq — not a lookalike interpreter — at <a href="https://jsonformatter.app">JSON Formatter App</a>. The <a href="https://jsonformatter.app/news/json-transform-jq-jsonpath">jq-wasm build</a> compiles jq 1.8.2, so the filter language behaves exactly like the CLI you know:</p>
<pre><code class="language-bash"># failed orders with their error codes
.map(select(.status == "failed") | {id, code: .error.code})

# group users by role, count each group
group_by(.role) | map({role: .[0].role, count: length})

# top 5 endpoints by latency
sort_by(-.latency_ms) | .[0:5] | map({endpoint, latency_ms})
</code></pre>
<p>Type a filter, see results live as you type, and every transform is one <code>Ctrl+Z</code> away — which matters more than it sounds when you're five refinements deep into a gnarly filter.</p>
<h2>Why WebAssembly instead of a JS reimplementation</h2>
<p>A handful of sites reimplement jq syntax in JavaScript. They're fine until they aren't: edge cases in string interpolation, <code>@base64</code>, <code>limit</code>, date functions, or streaming behavior drift from real jq, and your filter silently returns something <em>almost</em> right. With the compiled binary there's no drift to audit — it's jq's own C code answering.</p>
<h2>When JSONPath is the better hammer</h2>
<p>jq is for <em>transformation</em>; <a href="https://jsonformatter.app/news/jsonpath-live-evaluation">JSONPath</a> is for <em>extraction</em>. If your question is "give me every <code>$.orders[*].items[*].sku</code>", JSONPath is shorter to write and shows match counts with unwrapped single results. The panel flags honest errors instead of silently returning <code>[]</code> — the classic JSONPath failure mode that makes you think your path matched nothing when actually it was malformed.</p>
<h2>The part nobody talks about: where your payload goes</h2>
<p>A jq playground that runs your filter server-side has just received your JSON. If that payload is a production response — user records, tokens, internal IDs — you've shipped it to a stranger's logs. This isn't hypothetical; we covered <a href="https://jsonformatter.app/news/codebeautify-data-leak-explained">what the CodeBeautify leak exposed</a> in detail.</p>
<p>The whole point of running jq in WASM is that after the page loads, <strong>zero network requests leave the tab</strong>. Don't take my word for it — open DevTools → Network, clear it, paste your payload, run a filter. That 10-second audit works on any tool; <a href="https://jsonformatter.app/news/secure-json-formatter-online">here's the full checklist</a> for evaluating whether an online formatter is safe with sensitive data.</p>
<h2>Quick reference</h2>
<table>
<thead>
<tr>
<th>Task</th>
<th>jq filter</th>
</tr>
</thead>
<tbody><tr>
<td>Pick a field</td>
<td><code>.user.email</code></td>
</tr>
<tr>
<td>Filter an array</td>
<td>`.[]</td>
</tr>
<tr>
<td>Reshape</td>
<td><code>map({name, city})</code></td>
</tr>
<tr>
<td>Group + count</td>
<td>`group_by(.role)</td>
</tr>
<tr>
<td>Sort desc</td>
<td><code>sort_by(-.score)</code></td>
</tr>
</tbody></table>
<p>Try it at <a href="https://jsonformatter.app">jsonformatter.app</a> — the transform panel sits next to the formatter and validator, so you can debug the syntax error and transform the payload in the same tab.</p>
]]></content:encoded></item><item><title><![CDATA[Your video calls stutter when the line gets busy — that's bufferbloat]]></title><description><![CDATA[Latency under load — bufferbloat — is why calls break during downloads. Diagnose it free in 25 seconds and fix it with router SQM.]]></description><link>https://easy28.hashnode.dev/your-video-calls-stutter-when-the-line-gets-busy-that-s-bufferbloat</link><guid isPermaLink="true">https://easy28.hashnode.dev/your-video-calls-stutter-when-the-line-gets-busy-that-s-bufferbloat</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[networking]]></category><category><![CDATA[performance]]></category><category><![CDATA[tutorials]]></category><dc:creator><![CDATA[easy28]]></dc:creator><pubDate>Sat, 31 Oct 2026 15:13:55 GMT</pubDate><content:encoded><![CDATA[<p>Picture this: you're on a video call, everything is smooth, then a teammate kicks off a big <code>git clone</code> — and your face turns into a slideshow. Your download speed didn't drop. Your <strong>latency</strong> did. This is bufferbloat, and it's the most common "my internet is fine but feels awful" problem in home networks.</p>
<h2>What's actually happening</h2>
<p>Your router has finite buffers. When a download saturates the line, packets (including those tiny latency-sensitive ones carrying your voice) queue up behind bulk traffic. The result: ping goes from 8 ms to 300+ ms while the line is busy. Speed tests never show this, because they measure speed while idle.</p>
<p>The fix starts with <strong>measuring it</strong>, which most tools don't do. <a href="https://speedpulse.tech">SpeedPulse</a> is a free browser-based speed test that measures idle latency, <strong>jitter</strong> (variation in ping), and <strong>bufferbloat</strong> (how much latency grows under load), then grades each A+ through F in plain English. No signup, no app, no cookies.</p>
<h2>How to diagnose in 25 seconds</h2>
<ol>
<li>Open <a href="https://speedpulse.tech">speedpulse.tech</a> and run a test while your network is idle — note the bufferbloat grade.</li>
<li>Start a big download in another tab (or have someone stream 4K) and run it again.</li>
<li>If your latency under load jumps 10x, congratulations: it's bufferbloat, not your ISP's bandwidth.</li>
</ol>
<p>While you're at it, check whether your ISP is gaming the test: SpeedPulse pings <strong>8 independent CDN edges</strong> after each test. If the speed-test server is dramatically faster than every other edge, your ISP is likely prioritizing speed-test traffic. There's a full write-up of that methodology <a href="https://speedpulse.tech/blog/how-speedpulse-measures-jitter-and-bufferbloat/">on the SpeedPulse blog</a>.</p>
<h2>What actually fixes it</h2>
<ul>
<li><strong>Enable SQM/QoS on your router</strong> (OpenWrt's cake/sfq or a Smart Queue on stock firmware) — this is the single biggest win, it keeps buffers short so bulk traffic can't starve latency.</li>
<li><strong>Prefer wired</strong> for the machine doing calls; Wi-Fi adds jitter even on a clean line.</li>
<li><strong>Cap background sync</strong> (cloud drives, Steam, OS updates) during meetings.</li>
<li>If your ISP can't deliver sane latency under load even with SQM, that's a real signal when choosing providers.</li>
</ul>
<p>If you want a systematic walkthrough, I put together a <a href="https://easy28.github.io/internet-slow-checklist/">free "why is my internet slow" checklist</a> covering symptoms, measurement, and fixes.</p>
<h2>Why I built this</h2>
<p>I'm the author of SpeedPulse — I built it because raw Mbps numbers hide the problems people actually feel: stuttering calls, rubber-banding games, slow page loads on an "idle-fast" line. It also shows verdicts for streaming, gaming, and video calls instead of making you interpret raw numbers. If you compare tools, I also published <a href="https://speedpulse.tech/blog/speed-test-comparison-ookla-fast-waveform/">how it stacks up against Ookla, Fast.com and Waveform</a>, and a plain-English explainer of <a href="https://speedpulse.tech/blog/what-is-bufferbloat/">what bufferbloat actually is</a>.</p>
<p>Run one test today — if your bufferbloat grade is C or worse, the SQM fix above will genuinely change your calls. Happy to answer methodology questions in the comments.</p>
]]></content:encoded></item></channel></rss>