<?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: ludy.dev</title>
    <description>The latest articles on DEV Community by ludy.dev (@lyyluca).</description>
    <link>https://dev.to/lyyluca</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%2F3749447%2F63cf697c-bb8e-4482-8c24-c27b42cf2024.png</url>
      <title>DEV Community: ludy.dev</title>
      <link>https://dev.to/lyyluca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lyyluca"/>
    <language>en</language>
    <item>
      <title>How I built a static PDF generation pipeline for clean printable templates</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Wed, 03 Jun 2026 09:28:48 +0000</pubDate>
      <link>https://dev.to/lyyluca/how-i-built-a-static-pdf-generation-pipeline-for-clean-printable-templates-bfm</link>
      <guid>https://dev.to/lyyluca/how-i-built-a-static-pdf-generation-pipeline-for-clean-printable-templates-bfm</guid>
      <description>&lt;h2&gt;
  
  
  Automating the Generation Script
&lt;/h2&gt;

&lt;p&gt;I wrote a Node.js build script that spins up Playwright, visits the local rendering routes, and prints them to PDF format:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generatePDF&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="nx"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&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;browser&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;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&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;goto&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;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&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;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 'Letter' or 'A4'&lt;/span&gt;
    &lt;span class="na"&gt;landscape&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="na"&gt;printBackground&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="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;This static pipeline outputs clean, minimal vector PDFs directly into my public directory during the build process, ensuring fast static page delivery without on-the-fly PDF generation overhead. Let me know what you think of this approach!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>pdf</category>
      <category>staticgen</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Programmatically Generating Print-Ready Vector PDFs for the Web</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Mon, 01 Jun 2026 09:55:32 +0000</pubDate>
      <link>https://dev.to/lyyluca/programmatically-generating-print-ready-vector-pdfs-for-the-web-3g7o</link>
      <guid>https://dev.to/lyyluca/programmatically-generating-print-ready-vector-pdfs-for-the-web-3g7o</guid>
      <description>&lt;p&gt;This math ensures that regardless of user browser DPI, the final file looks identical on every machine and prints beautifully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero Server Load
&lt;/h2&gt;

&lt;p&gt;Because the puzzle data structures are lightweight JSON matrices, the rendering layer runs entirely on client-side JS using modern web canvas and PDF synthesis tools. This means hosting costs scale close to $0, allowing the platform to remain completely free and ad-light for parents and teachers.&lt;/p&gt;

&lt;p&gt;I would love to hear your thoughts on optimization techniques for programmatic puzzle generation or dynamic PDF layouts!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>pdf</category>
    </item>
    <item>
      <title>Under the Hood: Building a zero-signup, client-side home repair cost estimator</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Mon, 01 Jun 2026 09:54:33 +0000</pubDate>
      <link>https://dev.to/lyyluca/under-the-hood-building-a-zero-signup-client-side-home-repair-cost-estimator-j9</link>
      <guid>https://dev.to/lyyluca/under-the-hood-building-a-zero-signup-client-side-home-repair-cost-estimator-j9</guid>
      <description>&lt;p&gt;As developers, we often build SaaS projects with complex databases, user auth, and heavy backend logic. But sometimes, the most useful thing you can build is a highly reactive, pure static calculator designed to solve a real-world headache.&lt;/p&gt;

&lt;p&gt;After receiving some absurdly high quotes to fix my home's damp crawl space, I decided to analyze contractor pricing algorithms and build a client-side cost estimator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno8yeic83f90ftuj7a1o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fno8yeic83f90ftuj7a1o.jpg" alt="Featured Image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;I kept things incredibly lightweight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework&lt;/strong&gt;: Next.js (Static HTML Export)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling&lt;/strong&gt;: Tailwind CSS for responsive grid layouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management&lt;/strong&gt;: React Hooks (clean and simple, no heavy state libraries needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because I wanted this to be a fast utility, I avoided any backend databases or user authentication. The mathematical model runs entirely in the user's browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mathematical Challenge
&lt;/h2&gt;

&lt;p&gt;The hardest part was translating messy, real-world construction logic into predictable JavaScript formulas. Contractor pricing isn't linear. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Material waste&lt;/strong&gt;: You can't just buy the exact square footage of a 20-mil vapor barrier. You have to account for a 10% overlap on seams and vertical wall runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labor scaling&lt;/strong&gt;: Small crawl spaces have a higher labor cost per square foot because setting up equipment and maneuvering in tight spaces takes a fixed baseline of time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equipment thresholds&lt;/strong&gt;: A dehumidifier isn't sized linearly; it scales based on total cubic volume (sq ft * height) and local humidity basements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I mapped out these thresholds using static lookup matrices, ensuring the generated estimates match real-world bids within a 5-10% margin of error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bypassing the Lead-Gen Trap
&lt;/h2&gt;

&lt;p&gt;Most online home-improvement calculators are actually lead-generation funnels disguised as tools. They force you to enter your phone number so they can sell your info to local contractors. &lt;/p&gt;

&lt;p&gt;I deliberately built this tool to run entirely client-side with zero tracking, zero storage, and zero email gates. The feedback has been awesome, proving that people still appreciate pure utility tools on the web.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>jamstack</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Tired of SEO Spam? Building a Static-First Directory for 85+ AI Tools</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Mon, 01 Jun 2026 09:49:50 +0000</pubDate>
      <link>https://dev.to/lyyluca/tired-of-seo-spam-building-a-static-first-directory-for-85-ai-tools-1c80</link>
      <guid>https://dev.to/lyyluca/tired-of-seo-spam-building-a-static-first-directory-for-85-ai-tools-1c80</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsn5xd8x8juftwzs9ds7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsn5xd8x8juftwzs9ds7.jpg" alt="Featured Image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have all been there. You need a simple background remover or want to compare DALL-E 3 pricing against Midjourney, but search results are clogged with heavily optimized SEO spam, aggressive paywalls, and outdated blog posts.&lt;/p&gt;

&lt;p&gt;I got tired of clicking through dozens of spammy sites just to find a simple, functional free stock photo resource or a reliable AI image upscaler. So, I did what any developer does: I spent a weekend building my own curated database hub, ImageNav, to solve this tool-fatigue problem once and for all.&lt;/p&gt;

&lt;p&gt;Here is how I engineered a lightning-fast, zero-database catalog of 85+ AI image tools that loads in milliseconds and costs nothing to host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Databases for Small Catalogs is an Anti-Pattern
&lt;/h2&gt;

&lt;p&gt;When building directory sites, the knee-jerk reaction is to spin up a PostgreSQL instance or hook up a heavy headless CMS. But for a dataset of ~100 curated tools, this adds unnecessary latency, architectural overhead, and database query costs.&lt;/p&gt;

&lt;p&gt;Instead, I opted for a static-first architecture using Next.js and structured JSON files. &lt;/p&gt;

&lt;p&gt;Every tool on ImageNav is represented as a typed object in a TypeScript schema. By storing the data locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Pages are compiled statically at build time. Search and filtering happen entirely client-side, making the UX feel instantaneous.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; The site is hosted on Vercel with zero database cold-start issues, completely within the free tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; Data updates are as simple as editing a local JSON file or merging a quick Git pull request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  High-Performance Client-Side Filtering
&lt;/h2&gt;

&lt;p&gt;To make sorting through 85+ tools seamless, I built a lightweight React Hook that manages multi-select facets (Categories, Pricing Models, and Features). &lt;/p&gt;

&lt;p&gt;Instead of pulling in heavy third-party search libraries, I combined a lightweight regex fuzzy search with Native JS array filtering. Since the dataset size is highly optimized, these array operations execute in under 2ms even on low-end mobile devices, keeping the UI highly responsive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating Link &amp;amp; Integrity Checks
&lt;/h2&gt;

&lt;p&gt;One of the biggest issues with curated directories is link rot and outdated pricing. To tackle this, I wrote a Node.js script that runs weekly via GitHub Actions. It sends lightweight HEAD requests to all cataloged URLs to verify status codes and flags any tool that goes offline or changes redirects.&lt;/p&gt;

&lt;p&gt;ImageNav is now live at &lt;a href="https://imagenav.net" rel="noopener noreferrer"&gt;https://imagenav.net&lt;/a&gt;. If you are looking for a clean, ad-free way to find your next design or AI generator tool, check it out and let me know what you think of the UX!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a 100% Client-Side HEIC to JPG Converter: Zero Servers, Zero Uploads</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:38:48 +0000</pubDate>
      <link>https://dev.to/lyyluca/building-a-100-client-side-heic-to-jpg-converter-zero-servers-zero-uploads-f63</link>
      <guid>https://dev.to/lyyluca/building-a-100-client-side-heic-to-jpg-converter-zero-servers-zero-uploads-f63</guid>
      <description>&lt;p&gt;Every time I Airdrop photos from my iPhone to my Windows machine, I run into the same annoying problem: &lt;code&gt;.heic&lt;/code&gt; files. They don’t open natively on most Windows photo viewers, and converting them is a massive pain. &lt;/p&gt;

&lt;p&gt;When I looked for online converters, I was shocked by how sketchy they were. Most of them required me to upload my private photos to their servers, limited me to 5 files per batch unless I paid, or spammed me with intrusive ads. As a developer, I knew there had to be a better, more secure way to handle this. Why send megabytes of private image data to a remote server when our modern browsers have more than enough computing power to do it locally?&lt;/p&gt;

&lt;p&gt;So, I spent my weekend building a 100% client-side, offline-first HEIC to JPG converter: heictojpgfree.net.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr3jekf6qr1pgfkgqzb8e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr3jekf6qr1pgfkgqzb8e.jpg" alt="Featured Image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack: How it Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;To make this tool lightning-fast and completely private, I designed it to run entirely in the user's browser. Here is the technical breakdown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;WebAssembly (Wasm) + libheif&lt;/strong&gt;: Browsers don't natively support decoding HEIC/HEIF files yet. To bypass this, I compiled &lt;code&gt;libheif&lt;/code&gt; (a popular C++ HEIF decoder) into WebAssembly using Emscripten. This allows the browser to parse the HEIC container and extract the raw image data at near-native speeds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML5 Canvas&lt;/strong&gt;: Once the Wasm module decodes the raw pixel data, it transfers the byte array to an offscreen HTML5 &lt;code&gt;canvas&lt;/code&gt; element. From there, I use &lt;code&gt;canvas.toBlob()&lt;/code&gt; to compress and export it as a standard JPEG file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File System Access API&lt;/strong&gt;: Users can drag and drop an entire folder of HEIC files. The tool processes them sequentially or concurrently based on the hardware threads available.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Architectural Challenges
&lt;/h2&gt;

&lt;p&gt;Building this wasn't without its hurdles. During testing, I noticed two major issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory Leaks&lt;/strong&gt;: WebAssembly doesn't have automatic garbage collection for C++ pointers. When batch-converting 50+ high-res iPhone photos, the browser tab would frequently crash due to out-of-memory (OOM) errors. I had to strictly manage memory allocation, ensuring every decoded frame and allocation pointer was manually freed immediately after drawing to the canvas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Thread Blocking&lt;/strong&gt;: Initially, decoding heavy HEIC files on the main thread made the UI completely unresponsive. I resolved this by offloading the Wasm decoding process to &lt;strong&gt;Web Workers&lt;/strong&gt;. This keeps the browser UI running at a buttery-smooth 60fps, even while batch-processing dozens of images.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Give it a Spin
&lt;/h2&gt;

&lt;p&gt;The result is a clean, minimal utility at heictojpgfree.net. No signups, no paywalls, and absolutely no data leaves your machine. &lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts on the performance! How do you handle heavy image processing on the frontend? Drop your feedback or questions in the comments below!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webassembly</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Fast, Ad-Free Gacha Simulator and Pity Tracker: The Tech Stack and Math Behind GachaLab</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:38:43 +0000</pubDate>
      <link>https://dev.to/lyyluca/building-a-fast-ad-free-gacha-simulator-and-pity-tracker-the-tech-stack-and-math-behind-gachalab-1o71</link>
      <guid>https://dev.to/lyyluca/building-a-fast-ad-free-gacha-simulator-and-pity-tracker-the-tech-stack-and-math-behind-gachalab-1o71</guid>
      <description>&lt;p&gt;By isolating this state client-side using local storage, users can track their actual in-game pity count securely without needing a heavy database back-end or login flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping SEO Clean Without Bloat
&lt;/h2&gt;

&lt;p&gt;A major challenge was indexing highly dynamic content (like daily changing redemption codes). Instead of pulling these via heavy client-side API requests on load, we set up a nightly headless build process that fetches the latest data, updates our static markdown schemas, and redeploys the site automatically. The result? 100/100 Lighthouse performance scores and instant navigation.&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts on this setup. How do you usually handle dynamic math-heavy features in static site generators? Drop a comment below!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>gamedev</category>
      <category>performance</category>
    </item>
    <item>
      <title>Rendering Custom Tesla Wraps in the Browser: A Deep Dive into Real-Time 3D Canvas Mapping</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:28:13 +0000</pubDate>
      <link>https://dev.to/lyyluca/rendering-custom-tesla-wraps-in-the-browser-a-deep-dive-into-real-time-3d-canvas-mapping-3b9i</link>
      <guid>https://dev.to/lyyluca/rendering-custom-tesla-wraps-in-the-browser-a-deep-dive-into-real-time-3d-canvas-mapping-3b9i</guid>
      <description>&lt;p&gt;I've always loved messing around with the Toybox Paint Shop inside my Tesla, but trying to visualize how a custom wrap, pattern, or color shift would look on a real Model 3 or Cybertruck using just a flat color picker is incredibly frustrating. I wanted a way to design and preview complex patterns, matte finishes, and multi-tone wraps in real-time, then grab the exact configuration files instantly. &lt;/p&gt;

&lt;p&gt;Since nothing clean existed online, I built &lt;a href="https://teslapaintshop.org" rel="noopener noreferrer"&gt;TeslaPaintShop&lt;/a&gt; over a couple of weekends. Here is how I solved the technical challenges of rendering complex automotive wraps dynamically in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoipv9almxqo45czh1ty.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoipv9almxqo45czh1ty.jpg" alt="Featured Image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Tech Stack
&lt;/h2&gt;

&lt;p&gt;To keep the application lightweight, fast, and entirely client-side, I went with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Three.js / WebGL&lt;/strong&gt; for the 3D rendering pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite + Vanilla JS&lt;/strong&gt; to avoid framework overhead and keep the bundle size tiny.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML5 Canvas API&lt;/strong&gt; for real-time procedural texture and pattern generation before projecting them onto the 3D model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenge 1: Optimizing 3D Vehicle Meshes for the Web
&lt;/h2&gt;

&lt;p&gt;The biggest hurdle was asset size. Standard CAD models of a Tesla Model Y or Cybertruck are hundreds of megabytes. Downloading those on a mobile connection is a terrible user experience. &lt;/p&gt;

&lt;p&gt;I imported the raw models into Blender and aggressively decimated the geometry. I reduced the polygon count by over 85% while preserving sharp feature lines using split-normals. To handle different body panels, I separated the meshes into logical components (hood, doors, roof, trunk) so users could apply distinct materials to individual parts of the car. The final GLB assets are under 1.8MB each!&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 2: Dynamic Wrap &amp;amp; Pattern Mapping
&lt;/h2&gt;

&lt;p&gt;Applying a flat color is easy. Applying a seamless repeating wrap pattern (like carbon fiber, camo, or custom decals) across complex curved car panels is where it gets tricky. &lt;/p&gt;

&lt;p&gt;Instead of pre-rendering texture maps, I used dynamic HTML5 canvases as texture sources. When a user tweaks a pattern scale, angle, or metallic finish:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The app draws the pattern procedurally onto a hidden 2D canvas.&lt;/li&gt;
&lt;li&gt;It flags the Three.js &lt;code&gt;CanvasTexture&lt;/code&gt; with &lt;code&gt;needsUpdate = true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The WebGL shader recalculates the UV wrapping in real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the Cybertruck, the flat stainless steel panels were a breeze. But mapping wraps onto the curved wheel arches of the Model 3 required precision UV mapping to avoid ugly texture stretching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 3: Generating Toybox Compatible Outputs
&lt;/h2&gt;

&lt;p&gt;The final step was allowing users to export their designs. Since the Tesla custom toybox system reads specific color codes and configuration setups, the app translates the visual material properties (roughness, metalness, and hex colors) into instant, compatible layout files that match the exact parameters expected by the car’s system.&lt;/p&gt;

&lt;p&gt;I'd love to get your feedback on the rendering performance! Check it out at &lt;a href="https://teslapaintshop.org" rel="noopener noreferrer"&gt;teslapaintshop.org&lt;/a&gt; and let me know what you think of the WebGL material transitions in the comments.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webgl</category>
      <category>threejs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Bridging the language gap: Under the hood of an AI-powered game UI translator</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:28:08 +0000</pubDate>
      <link>https://dev.to/lyyluca/bridging-the-language-gap-under-the-hood-of-an-ai-powered-game-ui-translator-ljn</link>
      <guid>https://dev.to/lyyluca/bridging-the-language-gap-under-the-hood-of-an-ai-powered-game-ui-translator-ljn</guid>
      <description>&lt;p&gt;When NetEase relaunched World of Warcraft in China (specifically the Titan Reforged / Project 80 Chrono servers), thousands of English-speaking players jumped in. The problem? Translating Chinese game UI, item tooltips, and GDKP chat logs in real-time is a complete nightmare. Standard OCR tools fail hard because they don't understand game-specific terminology (like "GDKP", "Chrono scaling", or class abbreviations).&lt;/p&gt;

&lt;p&gt;So, I built a tailored web portal at titanreforged.com, featuring a custom AI-powered screenshot translator. Here’s how I put the technical pipeline together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7pafpazwzimuxzf00xx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7pafpazwzimuxzf00xx.jpg" alt="Featured Image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Low-Res MMO Fonts &amp;amp; Slang
&lt;/h2&gt;

&lt;p&gt;Game screenshots are notoriously hard to parse for standard OCR models. The fonts are styled, pixelated, and overlaid on complex, high-contrast backgrounds. Traditional OCR often outputs garbage text or fails entirely.&lt;/p&gt;

&lt;p&gt;Worse, standard translators fail at gaming slang. If a Chinese player writes "来个强力糖门", a generic translator translates it literally as "Come strong sugar gate". But in WoW speak, it actually means "Looking for a Warlock (who can create soulwells/healthstones)".&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: OCR + LLM Pipeline
&lt;/h2&gt;

&lt;p&gt;To solve this, I designed a two-stage processing pipeline using Next.js, HTML5 Canvas, and a serverless backend connecting to a custom-tuned LLM.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client-Side Canvas Preprocessing&lt;/strong&gt;: &lt;br&gt;
Before sending anything to the server, the user pastes a screenshot. I use HTML5 Canvas to convert the image to grayscale, increase contrast, and invert colors. This simple step improved OCR recognition accuracy by over 40%.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vision-to-Text Processing&lt;/strong&gt;: &lt;br&gt;
The processed image is sent to an edge API route. We run it through a vision model optimized for simplified Chinese characters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context-Aware LLM Translation&lt;/strong&gt;: &lt;br&gt;
Instead of piping raw text directly to a translation API, we pass the OCR output to an LLM with a specialized system prompt. The prompt includes a mapping dictionary of Chinese WoW terminology, dungeon names, and raid slang.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimizing for Speed and Cost
&lt;/h2&gt;

&lt;p&gt;Querying LLMs for every single screenshot gets expensive fast. To keep the site fast and free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redis Caching&lt;/strong&gt;: Exact matches of OCR strings bypass the LLM entirely and fetch cached translation mappings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debounced Requests&lt;/strong&gt;: On the frontend, user inputs are strictly debounced to prevent API abuse during rapid pasting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a clean, instant translator helper that turns Chinese raid listings into readable English in under 800ms. I'm currently looking to open-source the translation mapping dictionary. What do you think about this hybrid OCR setup? Drop your architectural suggestions below!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>ocr</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a zero-dependency, sub-100ms weight converter: Why client-side state is all you need</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:26:22 +0000</pubDate>
      <link>https://dev.to/lyyluca/building-a-zero-dependency-sub-100ms-weight-converter-why-client-side-state-is-all-you-need-4ipl</link>
      <guid>https://dev.to/lyyluca/building-a-zero-dependency-sub-100ms-weight-converter-why-client-side-state-is-all-you-need-4ipl</guid>
      <description>&lt;p&gt;This keeps the conversions accurate to 4 decimal places without adding layout shifts or overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bi-directional Instant State Binding
&lt;/h2&gt;

&lt;p&gt;Instead of having a single "Convert" button, the site calculates as you type. I mapped every input field (kilograms, pounds, stones, ounces, grams) to a single base unit value in grams. &lt;/p&gt;

&lt;p&gt;Whenever any input changes, the script calculates the base value in grams and instantly updates all other fields dynamically:&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;updateAllFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseGrams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activeInput&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;units&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;unit&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;activeInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;roundTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baseGrams&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;units&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;conversionFactor&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This unidirectional flow keeps the UI reactive and predictable with less than 60 lines of code.&lt;/p&gt;

&lt;p&gt;What are your thoughts on handling dynamic unit conversions? Would you have gone the Vanilla JS route or used a lightweight framework like Alpine? Let me know in the comments!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
      <category>css</category>
    </item>
    <item>
      <title>How I Engineered a Pixel-Perfect Layering Simulator for a Fashion Roguelike</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:26:13 +0000</pubDate>
      <link>https://dev.to/lyyluca/how-i-engineered-a-pixel-perfect-layering-simulator-for-a-fashion-roguelike-4pmp</link>
      <guid>https://dev.to/lyyluca/how-i-engineered-a-pixel-perfect-layering-simulator-for-a-fashion-roguelike-4pmp</guid>
      <description>&lt;h2&gt;
  
  
  Real-time Combo Calculation
&lt;/h2&gt;

&lt;p&gt;The real challenge was the calculation engine. Certain items trigger "Synergy Buffs" (e.g., matching a Kawaii headpiece with Gothic boots). Running O(N^2) checks on every UI click killed the frame rate on older mobile devices. &lt;/p&gt;

&lt;p&gt;To solve this, I compiled the game's combo rules into a bitwise matrix lookup table. Each outfit item is assigned a specific bitmask. Checking for combos is simplified to a series of fast bitwise &lt;code&gt;AND&lt;/code&gt; operations in the main React render loop, bringing the computation time down to less than 0.1ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping It Zero-Server
&lt;/h2&gt;

&lt;p&gt;Since I'm hosting this on a tight indie budget, I wanted the simulator to be completely serverless. The entire state of the Layering Simulator is serialized into a lightweight Base64 string appended to the URL path. This allows users to share their custom builds instantly with just a link, without me needing a database.&lt;/p&gt;

&lt;p&gt;Check out the live simulator at &lt;a href="https://dresstheduel.com" rel="noopener noreferrer"&gt;dresstheduel.com&lt;/a&gt; and let me know if you have any ideas on how to optimize asset pre-loading even further!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>gamedev</category>
      <category>react</category>
    </item>
    <item>
      <title>Curing SEO Spam: How I built a clean, markdown-driven directory of AI Video Tools and APIs</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 17:25:10 +0000</pubDate>
      <link>https://dev.to/lyyluca/curing-seo-spam-how-i-built-a-clean-markdown-driven-directory-of-ai-video-tools-and-apis-44b0</link>
      <guid>https://dev.to/lyyluca/curing-seo-spam-how-i-built-a-clean-markdown-driven-directory-of-ai-video-tools-and-apis-44b0</guid>
      <description>&lt;p&gt;Using standard markdown files for the data model yields major benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero Database Latency&lt;/strong&gt;: All pages are statically generated at build time. The site loads almost instantly because it's just static HTML and CSS served via CDN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git-based Workflow&lt;/strong&gt;: Updating the database is as simple as pushing a new markdown file or merging a PR. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust Local Filtering&lt;/strong&gt;: By compiling the frontmatter metadata into a single JSON index at build time, I can run ultra-fast client-side filtering and sorting without making any API requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tackling the Filtering Engine
&lt;/h2&gt;

&lt;p&gt;The biggest hurdle with static directories is handling complex multi-select filtering (e.g., finding a tool that has an API, offers a free tier, and supports image-to-video generation). &lt;/p&gt;

&lt;p&gt;Instead of relying on heavy client-side state libraries, I built a lightweight search index utility that parses the local JSON list on the client side using search parameters. The state is serialized directly into the URL, making every search filter deep-linkable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The goal is to keep this repository of AI video production software as clean, fast, and structured as possible. I want to build a public GitHub repository where developers can submit pull requests to add new tools or update pricing directly, avoiding the need for a complex admin portal.&lt;/p&gt;

&lt;p&gt;Check out the live version at &lt;a href="https://findaivideo.com" rel="noopener noreferrer"&gt;findaivideo.com&lt;/a&gt; and let me know your thoughts on this static markdown approach! How do you handle directory structures in your own side projects?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>markdown</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Building a zero-dependency Unicode text engine in the browser (and why clipboard APIs are still a pain)</title>
      <dc:creator>ludy.dev</dc:creator>
      <pubDate>Sun, 31 May 2026 15:59:00 +0000</pubDate>
      <link>https://dev.to/lyyluca/building-a-zero-dependency-unicode-text-engine-in-the-browser-and-why-clipboard-apis-are-still-a-4g25</link>
      <guid>https://dev.to/lyyluca/building-a-zero-dependency-unicode-text-engine-in-the-browser-and-why-clipboard-apis-are-still-a-4g25</guid>
      <description>&lt;p&gt;This works perfectly for sequential blocks. However, Unicode has some historical quirks (like the cursive "g" or "h" sometimes pointing to different ranges in the Letterlike Symbols block), which required custom fallback exceptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Glitch (Zalgo) Generator and Diacritics
&lt;/h2&gt;

&lt;p&gt;For the glitch and strikethrough generators, I had to work with Combining Diacritical Marks. These are characters designed to be rendered on top of, below, or through the preceding character. &lt;/p&gt;

&lt;p&gt;For the strikethrough tool, it's as simple as appending &lt;code&gt;\u0336&lt;/code&gt; after every character. For the glitch tool, I wrote a randomizer that stacks up to 15 different random combining marks (like &lt;code&gt;\u030d&lt;/code&gt; or &lt;code&gt;\u0311&lt;/code&gt;) on top of a single base letter. This forces the browser to render symbols that physically overflow the CSS line-height boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The iOS Clipboard Struggle
&lt;/h2&gt;

&lt;p&gt;Since the main user interaction is "one-click copy," I needed clipboard copying to be bulletproof. Modern browsers support &lt;code&gt;navigator.clipboard.writeText()&lt;/code&gt;, but iOS Safari is notoriously strict. If the copy action isn't directly inside a synchronous user-initiated call stack, iOS blocks it. &lt;/p&gt;

&lt;p&gt;To solve this, I built a reliable fallback:&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;copyToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&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;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;textArea&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="s2"&gt;textarea&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fixed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textArea&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Keeping it Ultra-Light
&lt;/h2&gt;

&lt;p&gt;By sticking to vanilla JS, avoiding modern UI frameworks (no React, no Vue), and optimizing our SVG icons, the entire site footprint is under 30KB. &lt;/p&gt;

&lt;p&gt;You can test the live tool out at &lt;a href="https://cursivetool.com/" rel="noopener noreferrer"&gt;cursivetool.com&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I would love to hear your thoughts on how you handle text manipulation and clipboard compatibility in your own utility projects!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>unicode</category>
      <category>css</category>
    </item>
  </channel>
</rss>
