<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Dylan Boudro</title>
        <link>portfolio-2025-git-master-starmorphs-projects.vercel.app</link>
        <description>Your blog description</description>
        <lastBuildDate>Fri, 17 Jul 2026 17:12:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Dylan Boudro</title>
            <url>portfolio-2025-git-master-starmorphs-projects.vercel.app/favicon.png</url>
            <link>portfolio-2025-git-master-starmorphs-projects.vercel.app</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Architecting a Text-to-Image Inference Platform]]></title>
            <link>portfolio-2025-git-master-starmorphs-projects.vercel.app/articles/architecting-a-text-to-image-inference-platform</link>
            <guid>portfolio-2025-git-master-starmorphs-projects.vercel.app/articles/architecting-a-text-to-image-inference-platform</guid>
            <pubDate>Tue, 20 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="PixelMuse Gallery - Showcase of AI-generated images" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p><a href="https://pixelmuse.studio">PixelMuse</a> is a text-to-image generation platform I built around type-safe model calls, authentication, credits, and polished image workflows for real users. The architecture matters because AI image products fail quickly when generation APIs, payments, user state, and error handling are treated as separate afterthoughts.</p>
<h2>Key facts</h2>
<table><thead><tr><th>Question</th><th>Answer</th><th>Evidence</th></tr></thead><tbody><tr><td>What is PixelMuse?</td><td>A web app for generating, browsing, and managing AI-generated images.</td><td>First-party product and implementation evidence from <a href="https://pixelmuse.studio">pixelmuse.studio</a>.</td></tr><tr><td>What framework anchors the app?</td><td>Next.js 14 with the App Router and TypeScript.</td><td>First-party implementation evidence, with App Router behavior documented by <a href="https://nextjs.org/docs/app">Next.js</a>.</td></tr><tr><td>How does the app call image models?</td><td>Through Replicate prediction endpoints and model-specific generation routes.</td><td>Replicate documents predictions and model prediction APIs in its <a href="https://replicate.com/docs/reference/http">HTTP API reference</a>.</td></tr><tr><td>How are users and credits managed?</td><td>Clerk handles authentication, while database-backed credit balances gate paid generation.</td><td>Clerk's Next.js SDK is documented in <a href="https://clerk.com/docs/nextjs/overview">Clerk Docs</a>; the credit system is first-party implementation evidence.</td></tr><tr><td>What is the durable product lesson?</td><td>AI generation needs a product system around the model: auth, billing, retries, state, gallery UX, and supportable errors.</td><td>First-party implementation evidence from building PixelMuse.</td></tr></tbody></table>
<h2>What problem was PixelMuse solving?</h2>
<p>PixelMuse was designed as more than a prompt box. The product needed account state, model selection, credit accounting, a browsable gallery, and clear failure states because image generation can be slow, expensive, and inconsistent. That pushed the architecture toward explicit boundaries between UI, generation routes, payments, auth, and persistence.</p>
<h2>Artist Gallery Pages</h2>
<img alt="PixelMuse Gallery - Showcase of AI-generated images" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-gallery-min.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<h2>Generating 3d Objects <code>.GBL</code> files</h2>
<p>Generated on PixelMuse using the <code>Trelis</code> AI model. This is first-party product evidence from the PixelMuse generation workflow.</p>
<img alt="PixelMuse Gallery - Showcase of AI-generated images" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" src="/articles/architecting-a-text-to-image-inference-platform/fireaxe.gif">
<h2>How does the application architecture work?</h2>
<p>Here's a high-level overview of how the different components interact:</p>
<img alt="Architecture diagram showing the flow between User, Frontend, Backend, Credit System, Replicate API, and Database" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="w-full" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=640&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=750&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=828&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=1080&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=1200&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=1920&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-adaptive-arch.svg&amp;w=1920&amp;q=75&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<h2>How are image generation calls made type-safe?</h2>
<p>The integration with Replicate's API is handled through a type-safe client generated from API metadata, then wrapped by application-level model configuration. Replicate exposes prediction and model prediction endpoints that map cleanly to this boundary.</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token operator">/</span>account
<span class="token operator">/</span>collections
<span class="token operator">/</span>collections<span class="token operator">/</span><span class="token punctuation">{</span>collection_slug<span class="token punctuation">}</span>
<span class="token operator">/</span>deployments
<span class="token operator">/</span>deployments<span class="token operator">/</span><span class="token punctuation">{</span>deployment_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>deployment_name<span class="token punctuation">}</span>
<span class="token operator">/</span>deployments<span class="token operator">/</span><span class="token punctuation">{</span>deployment_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>deployment_name<span class="token punctuation">}</span><span class="token operator">/</span>predictions
<span class="token operator">/</span>hardware
<span class="token operator">/</span>models
<span class="token operator">/</span>models<span class="token operator">/</span><span class="token punctuation">{</span>model_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>model_name<span class="token punctuation">}</span>
<span class="token operator">/</span>models<span class="token operator">/</span><span class="token punctuation">{</span>model_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>model_name<span class="token punctuation">}</span><span class="token operator">/</span>predictions
<span class="token operator">/</span>models<span class="token operator">/</span><span class="token punctuation">{</span>model_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>model_name<span class="token punctuation">}</span><span class="token operator">/</span>versions
<span class="token operator">/</span>models<span class="token operator">/</span><span class="token punctuation">{</span>model_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>model_name<span class="token punctuation">}</span><span class="token operator">/</span>versions<span class="token operator">/</span><span class="token punctuation">{</span>version_id<span class="token punctuation">}</span>
<span class="token operator">/</span>models<span class="token operator">/</span><span class="token punctuation">{</span>model_owner<span class="token punctuation">}</span><span class="token operator">/</span><span class="token punctuation">{</span>model_name<span class="token punctuation">}</span><span class="token operator">/</span>versions<span class="token operator">/</span><span class="token punctuation">{</span>version_id<span class="token punctuation">}</span><span class="token operator">/</span>trainings
<span class="token operator">/</span>predictions
<span class="token operator">/</span>predictions<span class="token operator">/</span><span class="token punctuation">{</span>prediction_id<span class="token punctuation">}</span>
<span class="token operator">/</span>predictions<span class="token operator">/</span><span class="token punctuation">{</span>prediction_id<span class="token punctuation">}</span><span class="token operator">/</span>cancel
<span class="token operator">/</span>trainings
<span class="token operator">/</span>trainings<span class="token operator">/</span><span class="token punctuation">{</span>training_id<span class="token punctuation">}</span>
<span class="token operator">/</span>trainings<span class="token operator">/</span><span class="token punctuation">{</span>training_id<span class="token punctuation">}</span><span class="token operator">/</span>cancel
<span class="token operator">/</span>webhooks<span class="token operator">/</span><span class="token keyword module">default</span><span class="token operator">/</span>secret
</code></pre>
<h2>Project Structure</h2>
<p>The application follows a clean, modular structure:</p>
<pre><code>src/
├── app/
│   ├── page.tsx                    # Home/generation page
│   ├── layout.tsx                  # Root layout
│   ├── pricing/
│   │   └── page.tsx               # Pricing page
│   └── api/
│       ├── auth/
│       │   └── webhook/route.ts   # Clerk webhook handler
│       ├── credits/
│       │   ├── check/route.ts     # Check credit balance
│       │   ├── consume/route.ts   # Consume credits
│       │   └── refresh/route.ts   # Refresh daily credits
│       ├── generate/
│       │   └── [model]/route.ts   # Image generation endpoints
│       └── payments/
│           └── webhook/route.ts   # Stripe webhook handler
├── components/
│   ├── ui/                        # shadcn/ui components
│   ├── layout/
│   │   ├── header.tsx            # Main nav with credits/auth
│   │   └── footer.tsx            # Site footer
│   ├── auth/
│   │   ├── auth-button.tsx       # Sign in/out button
│   │   └── user-profile.tsx      # User profile dropdown
│   ├── credits/
│   │   ├── credit-display.tsx    # Credit balance + buy button
│   │   └── credit-history.tsx    # Transaction history
│   ├── generation/
│   │   ├── prompt-form.tsx       # Text input + generate button
│   │   ├── model-select.tsx      # Model dropdown with Pro tags
│   │   └── result-display.tsx    # Generated image display
│   └── pricing/
│       ├── package-card.tsx      # Credit package display
│       └── pricing-grid.tsx      # Grid of packages
├── lib/
│   ├── db/
│   │   ├── schema.ts             # Drizzle schema
│   │   └── index.ts              # Database utilities
│   ├── replicate/
│   │   ├── client.ts             # Replicate API client
│   │   ├── models.ts             # Model configurations
│   │   └── types.ts              # Replicate API types
│   ├── auth/
│   │   └── clerk-utils.ts        # Clerk helpers
│   └── utils/
│       ├── error-handling.ts     # Error utilities
│       └── toast.ts              # Toast notifications
├── config/
│   ├── site.ts                   # Site configuration
│   ├── credits.ts                # Credit system config
│   └── models.ts                 # AI model config
└── styles/
    └── globals.css               # Global styles
</code></pre>
<h2>The Foundation: Next.js 14 and TypeScript</h2>
<p>I chose Next.js 14 as the foundation for PixelMuse, using the App Router, React Server Components, and server-side route handlers where they simplified the product boundary. The entire application is built with TypeScript, keeping type safety visible from database schema to API endpoints. Next.js documents the App Router as its file-system routing model for layouts, pages, and server/client composition.</p>
<p>Key architectural decisions included:</p>
<ul>
<li>Using pnpm for faster, more efficient package management</li>
<li>Leveraging server components by default for optimal performance</li>
<li>Strict TypeScript configuration throughout the codebase</li>
</ul>
<h2>Authentication and User Management</h2>
<p>For user authentication, I integrated Clerk, which provides Next.js components, hooks, and server helpers for authentication. User records are synced into Supabase through a webhook so the product can connect identity to credits, generation history, and billing state.</p>
<h2>Database and Type Safety</h2>
<p>The database layer is built on <code>Supabase</code> with <code>Drizzle ORM</code>. Supabase provides the hosted Postgres database, while Drizzle keeps SQL access typed in application code.</p>
<ul>
<li>Type-safe database queries and schema management</li>
<li>Real-time credit balance updates</li>
<li>Efficient user data synchronization</li>
</ul>
<h2>Credit System Implementation</h2>
<p>One of the core features is the credit system. Users can purchase credits in packages, and the system automatically deducts credits from their balance when an image is generated. I used Drizzle, Clerk, and Stripe to implement this.</p>
<h2>Decision record: what I learned building PixelMuse</h2>
<p><strong>Treat the model as one dependency, not the product.</strong> The hard parts were around the model call: authentication, credit checks, idempotent payment state, webhook handling, image persistence, loading states, and recoverable errors.</p>
<p><strong>Keep model configuration explicit.</strong> A product with multiple image and 3D generation models needs clear model metadata, supported inputs, cost assumptions, and UI affordances. Hiding that inside generic request handlers makes the product harder to price and debug.</p>
<p><strong>Make billing observable from day one.</strong> Credits are revenue infrastructure, not just UI state. Stripe, Clerk, and the database need shared identifiers so revenue events can be traced back to a real user and generation workload.</p>
<p><strong>Use type safety at boundaries.</strong> The highest-leverage typing was not complex generics. It was validating incoming webhook payloads, generation request bodies, model names, and database writes before they crossed system boundaries.</p>
<p>Source references for the platform choices: <a href="https://nextjs.org/docs/app">Next.js App Router</a>, <a href="https://clerk.com/docs/nextjs/overview">Clerk Next.js SDK</a>, <a href="https://replicate.com/docs/reference/http">Replicate HTTP API</a>, <a href="https://supabase.com/docs/guides/database">Supabase database docs</a>, <a href="https://orm.drizzle.team/docs/overview">Drizzle ORM overview</a>, and <a href="https://docs.stripe.com/webhooks">Stripe webhooks</a>.</p>
<h2>Looking Forward</h2>
<p>Building PixelMuse has been an exciting journey in creating a type-safe, modern web application. The architecture decisions I made have resulted in a robust platform that's easy to maintain and extend.</p>
<p>Some key learnings:</p>
<ul>
<li>The importance of proper type definitions throughout the stack</li>
<li>Value of proper error handling and user feedback</li>
</ul>
<p>The platform continues to evolve, and I'm excited to add more features while maintaining the high standards of type safety and user experience that have been established.</p>]]></content:encoded>
            <author>dylan@starmorph.com (Dylan Boudro)</author>
        </item>
        <item>
            <title><![CDATA[Building MermaidEditor.io: From Side Project to 3,000 Monthly Users in 30 Days]]></title>
            <link>portfolio-2025-git-master-starmorphs-projects.vercel.app/articles/building-mermaid-editor</link>
            <guid>portfolio-2025-git-master-starmorphs-projects.vercel.app/articles/building-mermaid-editor</guid>
            <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="MermaidEditor.io — the full editor interface with code, live preview, and styling panel" loading="lazy" width="1200" height="675" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p><a href="https://mermaideditor.io">MermaidEditor.io</a> is a free Mermaid diagram editor I built to make text-based diagrams faster to write, debug, and export. The product matters because Mermaid is widely used in developer documentation, but GitHub's native Mermaid rendering gives limited feedback when syntax breaks.</p>
<h2>Key facts</h2>
<table><thead><tr><th>Question</th><th>Answer</th><th>Evidence</th></tr></thead><tbody><tr><td>What is MermaidEditor.io?</td><td>A browser-based editor for writing, previewing, repairing, and exporting Mermaid diagrams.</td><td>First-party product evidence from the live editor at <a href="https://mermaideditor.io">mermaideditor.io</a>.</td></tr><tr><td>What problem does it solve?</td><td>It shortens the edit-commit-check loop created by broken Mermaid syntax in Markdown workflows.</td><td>GitHub documents native Mermaid rendering in Markdown, issues, pull requests, discussions, and wikis: <a href="https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams">GitHub Docs</a>.</td></tr><tr><td>What formats does it export?</td><td>PNG, SVG, and PDF exports.</td><td>First-party implementation evidence from the MermaidEditor export pipeline.</td></tr><tr><td>What happened in the first full month?</td><td>March 2026 usage reached 2,922 monthly active users, 25,361 pageviews, and 1,166 diagram exports.</td><td>First-party PostHog analytics evidence from the production product dashboard.</td></tr><tr><td>Why is AI discovery part of the story?</td><td>ChatGPT referred 24.8% of March 2026 traffic, making AI referrals a material acquisition channel.</td><td>First-party PostHog referrer evidence from March 2026.</td></tr></tbody></table>
<p>The editor solves this with live preview, inline error highlighting, 21 diagram types, theme presets, and export to PNG, SVG, and PDF — no login required. Mermaid itself is a text-based diagramming tool for documentation, and GitHub supports Mermaid diagrams directly in Markdown through fenced code blocks.</p>
<p>In its first full month (March 2026), MermaidEditor hit <strong>2,922 monthly active users</strong>, <strong>25,361 pageviews</strong>, and <strong>1,166 diagram exports</strong>. But the most surprising metric was where the traffic came from: <strong>25% of all visits were referred by ChatGPT</strong>.</p>
<p>This post covers the technical architecture, the hardest engineering problem I solved (the export pipeline), how I integrated AI features, the monetization experiments I ran, and what I learned about building for AI-driven discovery.</p>
<hr>
<h2>Why does Mermaid syntax fail silently in GitHub workflows?</h2>
<p>If you've ever written a Mermaid diagram in a GitHub README, you know the pain. You push a commit, navigate to the rendered Markdown, and see... a gray box. No error message. No line number. No hint at what went wrong.</p>
<pre class="language-mermaid"><code class="language-mermaid"><span class="token keyword">flowchart</span> TD
    A<span class="token text string">[Write Mermaid in README]</span> <span class="token arrow operator">--&gt;</span> B<span class="token text string">{Push to GitHub}</span>
    B <span class="token arrow operator">--&gt;</span> C<span class="token text string">[See blank gray box]</span>
    C <span class="token arrow operator">--&gt;</span> D<span class="token text string">[No error message]</span>
    D <span class="token arrow operator">--&gt;</span> E<span class="token text string">[Guess what's wrong]</span>
    E <span class="token arrow operator">--&gt;</span> A
</code></pre>
<p>Every Mermaid user has this workflow burned into muscle memory. The edit-commit-check loop is brutal when you're debugging syntax. I wanted a tool where you paste your code and immediately see what's wrong — with the actual error underlined, not hidden behind a silent render failure.</p>
<h2>How is MermaidEditor.io architected?</h2>
<p>The stack is intentionally modern but pragmatic:</p>
<ul>
<li><strong>Framework:</strong> Next.js 16 (App Router) with React 19 and TypeScript</li>
<li><strong>Editor:</strong> Monaco Editor with custom Mermaid language support via <code>monaco-mermaid</code></li>
<li><strong>Rendering:</strong> Mermaid.js 11.x running client-side in the browser</li>
<li><strong>Styling:</strong> Tailwind CSS 4 with shadcn/ui components</li>
<li><strong>Export:</strong> Canvas API (PNG), native SVG serialization, jsPDF (PDF)</li>
<li><strong>AI:</strong> Vercel AI SDK with Anthropic Claude and Google Gemini providers</li>
<li><strong>Database:</strong> Turso (libSQL) via Drizzle ORM for cloud diagram storage</li>
<li><strong>Auth:</strong> Clerk for optional user accounts</li>
<li><strong>Payments:</strong> Stripe for Pro subscriptions</li>
<li><strong>Analytics:</strong> PostHog (server + client) and Vercel Analytics</li>
<li><strong>Deployment:</strong> Vercel with automatic preview deployments</li>
</ul>
<p>The key architectural decision was making the editor fully functional without authentication. Users can open the site and start diagramming immediately — no signup wall, no "create an account to save" interstitial. Diagrams auto-save to local storage. Accounts are optional and unlock cloud sync.</p>
<p>This stack list is first-party implementation evidence from the MermaidEditor codebase and production deployment. Public source references for the core platform choices include <a href="https://github.com/mermaid-js/mermaid">Mermaid's project documentation</a>, <a href="https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams">GitHub's Mermaid Markdown support</a>, <a href="https://posthog.com/product/product-analytics">PostHog product analytics</a>, and <a href="https://vercel.com/docs/deployments/overview">Vercel deployments</a>.</p>
<h2>Why is Mermaid export harder than preview?</h2>
<p>Exporting a Mermaid diagram to a high-quality PNG, SVG, or PDF sounds straightforward. It isn't.</p>
<p>The core challenge is converting a Mermaid-rendered SVG into a raster image. The pipeline is: <strong>SVG → <code>&lt;img&gt;</code> element → <code>&lt;canvas&gt;</code> → file download</strong>. The critical step is loading the SVG string into an <code>&lt;img&gt;</code> tag so it can be drawn onto a canvas.</p>
<h3>The Approach That Won: Percent-Encoded Data URLs</h3>
<p>After testing multiple strategies, I settled on percent-encoded data URLs:</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">const</span> dataUrl <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">data:image/svg+xml;charset=utf-8,</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>svgString<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
</code></pre>
<p>This handles all Unicode characters (emoji, CJK, mathematical symbols), always fires <code>onload</code>/<code>onerror</code> callbacks, and has no practical size limits. Two other approaches were tested and rejected:</p>
<p><strong>Base64 data URLs</strong> failed on non-Latin1 characters — <code>btoa()</code> throws on emoji and CJK text, which is common in diagrams. They also hit browser URL length limits (~2MB) on large diagrams.</p>
<p><strong>Blob URLs</strong> (<code>URL.createObjectURL</code>) were worse: they can <strong>silently fail</strong> in Chrome and Safari. Neither <code>onload</code> nor <code>onerror</code> fires, leaving the export Promise hanging forever. The canvas also gets tainted due to CORS origin mismatch, making <code>toDataURL()</code> throw.</p>
<h3>htmlLabels: The Subtle Killer</h3>
<p>One of the trickiest bugs was that Mermaid renders node labels as <code>&lt;foreignObject&gt;</code> HTML by default (the <code>htmlLabels: true</code> setting). This looks great in the browser DOM but is invisible when the SVG is loaded into an <code>&lt;img&gt;</code> element — <code>&lt;foreignObject&gt;</code> content doesn't render in that context.</p>
<p>The fix: force <code>htmlLabels: false</code> during export, which tells Mermaid to use native SVG <code>&lt;text&gt;</code> elements instead. The preview still uses HTML labels for better styling, but exports get pure SVG text.</p>
<h3>Export Safeguards</h3>
<p>Every export includes a 30-second timeout on image loading, error tracking via PostHog (<code>export_failed</code> events with format, diagram type, and error details), and retry logic for transient failures.</p>
<p>The entire export pipeline is covered by Playwright tests that run against a real browser:</p>
<ul>
<li>PNG, SVG, and PDF export for 4 diagram sizes</li>
<li>File validity checks (PNG pixel analysis, SVG structure, PDF header)</li>
<li>Blankness detection — catching the "export succeeded but produced a white image" failure mode</li>
<li>Console error monitoring</li>
</ul>
<pre class="language-bash"><code class="language-bash"><span class="token function">pnpm</span> test:exports  <span class="token comment"># runs on every PR touching export code</span>
</code></pre>
<h2>Why did AI repair matter more than AI generation?</h2>
<p>I integrated AI in two ways: <strong>AI Generate</strong> (create a diagram from a natural language prompt) and <strong>AI Repair</strong> (fix syntax errors in existing code).</p>
<p>The surprising finding from production data: <strong>AI Repair is used 5.7x more than AI Generate</strong>. In March, users triggered 362 repair requests vs. 63 generation requests. The completion rate for repair was 74%, while generation hit 98%.</p>
<p>Those usage numbers are first-party PostHog event evidence from the MermaidEditor production analytics dashboard.</p>
<p>This makes intuitive sense once you think about it. Most users arrive with existing Mermaid code that's broken — they pasted it from a ChatGPT response, copied it from documentation, or tweaked syntax that was working before. They don't need AI to create a diagram from scratch; they need AI to tell them why their bracket is in the wrong place.</p>
<p>The AI features use Vercel AI SDK with streaming responses. Repair sends the broken Mermaid code plus the exact error message to Claude, which returns corrected code with an explanation. It's essentially a specialized debugger.</p>
<pre><code>March 2026 AI Usage:
├── AI Repair:    362 requests (74.3% completion)
├── AI Fix All:   133 requests (56.4% completion)
└── AI Generate:   63 requests (98.4% completion)
</code></pre>
<h2>First Month Metrics</h2>
<p>Here's what the PostHog dashboard showed after the first full month of operation:</p>
<h3>User Growth</h3>
<table><thead><tr><th>Metric</th><th>Value</th></tr></thead><tbody><tr><td>Monthly Active Users</td><td><strong>2,922</strong></td></tr><tr><td>Peak Daily Active Users</td><td><strong>802</strong></td></tr><tr><td>Total New Users (5 weeks)</td><td><strong>3,151</strong></td></tr><tr><td>Total Pageviews (March)</td><td><strong>25,361</strong></td></tr><tr><td>Avg. Session Duration</td><td><strong>3–10 minutes</strong></td></tr></tbody></table>
<h3>Feature Usage</h3>
<table><thead><tr><th>Metric</th><th>Value</th></tr></thead><tbody><tr><td>Diagram Exports (March)</td><td><strong>1,166</strong></td></tr><tr><td>Chart Auto-Saves</td><td><strong>18,743</strong></td></tr><tr><td>Templates Used</td><td><strong>188</strong></td></tr><tr><td>AI Features Triggered</td><td><strong>558</strong></td></tr></tbody></table>
<h3>Export Format Breakdown</h3>
<p>PNG leads at 48%, followed closely by PDF at 43% — which surprised me. I expected PNG to dominate. The high PDF share suggests a significant portion of users are exporting diagrams for documentation, reports, and presentations rather than embedding in web content.</p>
<table><thead><tr><th>Format</th><th>Share</th></tr></thead><tbody><tr><td>PNG</td><td>47.9%</td></tr><tr><td>PDF</td><td>42.7%</td></tr><tr><td>SVG</td><td>8.5%</td></tr><tr><td>Markdown</td><td>0.9%</td></tr></tbody></table>
<h3>Conversion Funnel</h3>
<ul>
<li><strong>Editor Loaded:</strong> 2,268 users</li>
<li><strong>Export Completed:</strong> 126 users (5.56% conversion)</li>
<li><strong>Median Time to Export:</strong> 1 min 36 sec</li>
<li><strong>Average Time to Export:</strong> 14 min 2 sec</li>
</ul>
<p>The bifurcation between median and average tells an interesting story. Half the users know exactly what they need — paste code, export, done in 90 seconds. The other half spends significant time crafting and iterating on their diagrams.</p>
<h2>The ChatGPT Traffic Story</h2>
<p>The most remarkable finding was the traffic source breakdown:</p>
<table><thead><tr><th>Source</th><th>Share</th></tr></thead><tbody><tr><td>Direct</td><td>34.0%</td></tr><tr><td>Google Search</td><td>28.6%</td></tr><tr><td><strong>ChatGPT</strong></td><td><strong>24.8%</strong></td></tr><tr><td>Bing</td><td>2.7%</td></tr><tr><td>DuckDuckGo</td><td>2.0%</td></tr><tr><td>Perplexity AI</td><td>1.1%</td></tr><tr><td>Gemini</td><td>0.1%</td></tr></tbody></table>
<p><strong>A quarter of all traffic came from ChatGPT.</strong> When you combine all AI sources (ChatGPT, Perplexity, Gemini, Claude), AI chatbots account for roughly 26% of referrals.</p>
<p>This wasn't accidental. I invested in what's now called <strong>GEO (Generative Engine Optimization)</strong> from day one:</p>
<ol>
<li><strong><code>llms.txt</code></strong> — A structured text file at <code>/llms.txt</code> that tells AI crawlers exactly what the tool does, what diagram types it supports, and how to use it</li>
<li><strong><code>robots.txt</code> allows all AI crawlers</strong> — No blocking of GPTBot, ClaudeBot, or other AI user agents</li>
<li><strong>SEO landing pages per diagram type</strong> — 21 pages targeting queries like "mermaid flowchart editor", "mermaid sequence diagram online", each with examples and syntax guides</li>
<li><strong>Fact-dense, structured content</strong> — Instead of marketing prose, the landing pages use concrete claims: "21 diagram types", "PNG/SVG/PDF export", "no login required"</li>
</ol>
<p>The theory is that LLMs extract and recommend tools based on structured, authoritative content. When someone asks ChatGPT "how do I make a Mermaid flowchart", it recommends tools it has seen described clearly and specifically. Marketing fluff doesn't help — concrete capabilities do.</p>
<p>I wrote about this approach more broadly in the context of <a href="/articles/architecting-a-text-to-image-inference-platform">GEO optimization for personal sites</a>.</p>
<h2>Monetization: A/B Testing the Upgrade Path</h2>
<p>MermaidEditor uses a freemium model: the core editor is free forever, and Pro ($7/month or $39 lifetime) removes watermarks, unlocks high-resolution exports (up to 8x / 768 DPI), and adds 200 AI generations per month.</p>
<p>The conversion challenge with developer tools is that the free tier has to be genuinely useful — otherwise nobody sticks around to convert. I ran several A/B tests on the upgrade flow:</p>
<h3>Post-Export Modal Variants</h3>
<img alt="Post-export modal A/B test — split panel layout with diagram preview and upgrade CTA vs. receipt-style variant" loading="lazy" width="1200" height="600" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>The post-export modal appears after every free export with the watermark visible on the diagram. I tested two layouts:</p>
<ul>
<li><strong>Split Panel (Default):</strong> Shows the exported diagram preview alongside the upgrade pitch. Users can see their watermarked diagram and the value prop side by side.</li>
<li><strong>Receipt Variant:</strong> Treats the export like a receipt — shows format, resolution, watermark status, and a clear comparison of free vs. Pro capabilities.</li>
</ul>
<h3>Upgrade Modal Layouts</h3>
<img alt="Three upgrade modal variants being A/B tested — compact card, side-by-side comparison, and contextual prompt" loading="lazy" width="1200" height="600" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>I tested three distinct upgrade modal designs:</p>
<ul>
<li><strong>Compact Single Card:</strong> Focused on a single decision — pick monthly or annual, then checkout. Minimal friction, best for impulse conversions.</li>
<li><strong>Side-by-Side Comparison:</strong> Shows Free vs. Pro feature-by-feature. More information helps justify the price but can feel heavy as a modal.</li>
<li><strong>Contextual Upgrade Prompt:</strong> Tells the user <em>why</em> they're seeing this (e.g., "Watermark-free exports require Pro"). Changes message based on the trigger context.</li>
</ul>
<h3>Export Upgrade Banner Variants</h3>
<img alt="Three A/B test variants for the export upgrade banner — control with premium positioning, urgency with problem-aware framing, and benefit-focused with outcome-driven copy" loading="lazy" width="800" height="400" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>The export toolbar banner is the most visible upgrade surface — it appears every time a free user exports. Three copy strategies:</p>
<ul>
<li><strong>Control (Premium Positioning):</strong> "Remove watermark &amp; unlock print DPI" — neutral, feature-focused</li>
<li><strong>Urgency (Problem-Aware):</strong> "This export includes a watermark" — calls out the problem directly</li>
<li><strong>Benefit-Focused:</strong> "Export crisp PNGs at up to 8x resolution" — leads with the outcome</li>
</ul>
<h3>Navigation Layout Testing</h3>
<img alt="Nav layout A/B test — four variants testing Pro button placement and styling in the main navigation bar" loading="lazy" width="1200" height="400" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>I also tested where and how the Pro upgrade button appears in navigation — right side vs. left side, with vs. without the Learn/More dropdowns, and gradient button vs. standard styling. The hypothesis was that moving Pro into the left navigation (near the diagram type selector) would catch users earlier in their workflow.</p>
<h3>Early Results</h3>
<p>March numbers showed strong experiment exposure but modest conversion:</p>
<ul>
<li><strong>Watermark impressions:</strong> 813</li>
<li><strong>Upgrade modal views:</strong> 60</li>
<li><strong>Post-export modal views:</strong> 280</li>
<li><strong>First paying subscriber:</strong> 1</li>
</ul>
<p>One paying customer in month one is a validation signal, not a revenue story. The experiments are still gathering data, and the conversion path needs work — but the product-market signal is clear from the usage metrics.</p>
<h2>Global Reach from Day One</h2>
<p>The geographic distribution was surprisingly broad:</p>
<p>The US was the top country at 16% of traffic, followed by Brazil (11%), India (7%), Germany (6%), and France (6%). Over 25 countries had significant traffic. Edge browser accounted for 24% of sessions — unusually high and suggestive of enterprise/business users who are diagramming for work.</p>
<p>This distribution aligns with the GEO hypothesis: AI chatbots recommend tools globally without geographic bias. When ChatGPT tells a developer in São Paulo to "try mermaideditor.io", it has the same recommendation weight as for a developer in San Francisco.</p>
<h2>Decision record: what I would build the same way again</h2>
<p><strong>Monaco Editor</strong> over CodeMirror. Monaco's built-in TypeScript tooling, minimap, and multi-cursor support make it feel like VS Code in the browser. The <code>monaco-mermaid</code> package adds syntax highlighting and autocompletion for Mermaid syntax. The editor experience is one of the biggest differentiators.</p>
<p><strong>Client-side rendering</strong> for diagrams. Mermaid.js runs entirely in the browser. No server-side rendering, no headless Chrome, no API calls. This keeps latency at zero for preview updates and eliminates server costs for the core editing experience.</p>
<p><strong>PostHog over Mixpanel/Amplitude</strong> for analytics. Self-hostable, generous free tier, session replays, feature flags, and the A/B testing framework all in one platform. Being able to watch session recordings of users struggling with the export flow directly informed the UI improvements.</p>
<p><strong>Stripe for payments</strong> with a lifetime deal option ($39 one-time). Lifetime deals are controversial in SaaS, but for a tool with near-zero marginal cost per user, they're a strong acquisition lever early on. One-time payments have lower friction than subscriptions for developer tools.</p>
<h2>What's Next</h2>
<p>The immediate roadmap is focused on the conversion funnel — the product has strong engagement (18,743 chart saves, 3-10 minute sessions) but the editor-to-paying-customer path needs optimization. The A/B tests are still in early innings.</p>
<p>On the product side: team workspaces, version history, and a REST API for programmatic diagram generation are the most-requested features. The AI repair feature has the most growth potential — it's already the most-used AI feature and directly solves a real pain point.</p>
<p>The GEO strategy will continue to be a focus. When 25% of your traffic comes from AI chatbots, optimizing for AI discoverability is just as important as traditional SEO.</p>
<hr>
<p><em>MermaidEditor.io is free at <a href="https://mermaideditor.io">mermaideditor.io</a>. The source code is private, but I'm happy to discuss the architecture — <a href="https://x.com/starmorphai">reach out on X</a>.</em></p>]]></content:encoded>
            <author>dylan@starmorph.com (Dylan Boudro)</author>
        </item>
        <item>
            <title><![CDATA[Building a Helium IoT Antenna Network: From Radio Engineering to Cryptocurrency Rewards]]></title>
            <link>portfolio-2025-git-master-starmorphs-projects.vercel.app/articles/radio-engineering-helium-antenna</link>
            <guid>portfolio-2025-git-master-starmorphs-projects.vercel.app/articles/radio-engineering-helium-antenna</guid>
            <pubDate>Tue, 20 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<img alt="Installing Helium IoT antenna on rooftop for optimal coverage and mining rewards" loading="lazy" width="1800" height="1035" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>This article explains how I moved from curiosity about radio engineering into real Helium IoT antenna deployments across Arizona. It matters because Helium rewards are not just a crypto mechanic; they expose practical RF trade-offs around height, terrain, antenna pattern, weatherproofing, and measurable wireless coverage.</p>
<h2>Key facts</h2>
<table><thead><tr><th>Question</th><th>Answer</th><th>Evidence</th></tr></thead><tbody><tr><td>What network was this built for?</td><td>The Helium IoT Network, which uses LoRaWAN hotspots to provide wireless coverage for IoT devices.</td><td>Helium's IoT documentation describes the network and LoRaWAN role: <a href="https://docs.helium.com/iot">Helium IoT Network</a>.</td></tr><tr><td>What was the hands-on scope?</td><td>Rooftop and field deployments of Helium hotspots, antennas, enclosures, cable runs, grounding, and RF optimization in Arizona.</td><td>First-party deployment evidence from the antenna builds and photos in this article.</td></tr><tr><td>What technical signal did I optimize for?</td><td>Better witness quality, coverage reach, line of sight, and reliable operation in desert conditions.</td><td>First-party operational evidence, with Proof-of-Coverage mechanics documented by <a href="https://docs.helium.com/iot/proof-of-coverage">Helium</a>.</td></tr><tr><td>What frequency band mattered in North America?</td><td>Helium IoT deployments in the United States use the 915 MHz LoRaWAN region.</td><td>Helium's LoRaWAN documentation explains regional operation: <a href="https://docs.helium.com/iot/lorawan-on-helium">LoRaWAN on Helium</a>.</td></tr><tr><td>What was the durable engineering lesson?</td><td>Physical placement and RF path quality mattered more than chasing antenna gain alone.</td><td>First-party field evidence from repeated Arizona installs and troubleshooting.</td></tr></tbody></table>
<h2>How did I get interested in antennas?</h2>
<p>At the time I began exploring antennas and radio engineering for crypto mining, I was in a period of studying meditation. I was watching all the YouTube content I could find from yogis, saddhus, and Buddhist monks to learn about the world of meditation. I was deeply interested and spent all my free time diving in or practicing myself. At the time I was also building a meditation and yoga brand called Zafu, so I was encompassed in meditation from entertainment, to work, to practicing. I then noticed that a lot of renunciant Hindu monks, who spend much of their lives meditating, give away all of their belongings, yet they kept a picture of their guru as one of their few prized possessions. I found this intriguing. It made me think: what should the focus of my meditation be? What do I want to understand more deeply?</p>
<p>At the time, I chose Nikola Tesla. Not to worship, but to focus my study and growth of understanding on. I chose Tesla because similar to the transcendent nature of powerful yogis and meditators, Tesla seemingly broke the paradigm of what we thought was possible scientifically. I printed out a photo of Nikola Tesla and meditated with it. Upon exploring Tesla's work, I became more fascinated with electromagnetics, energy, and physics. I purchased a small tabletop antenna and placed it next to Tesla. This eventually primed my brain to jump into Helium when I learned that a cryptocurrency network was using radio coverage as part of its economic design.</p>
<p>I purchased a Helium miner. On the first few days I set it up in 2021, it was earning a notable amount of USD with the stock antenna. I decided I needed to upgrade my antenna to improve my earnings. So I searched for a radio shop and drove to one of the locations that showed up on Google. I was expecting a Radio Shack; what I found was a CB radio shop for a semi-truck stop deep in the valley of Phoenix, Arizona. I walked in and said I was looking for an antenna for my crypto miner.</p>
<p>In 2021, I wasn't expecting much of a response to this out-of-the-ordinary request. The shop owner was a curious engineer and found it fascinating. He offered to let me set the miner up on his roof, explaining that antenna height was one of the key variables for maximizing signal distance. This became the first of 50+ sessions I spent working with the owner of the CB shop to brainstorm, design, install, manage, upgrade, and fix Helium antennas.</p>
<h2>The Helium Network: Decentralized IoT Infrastructure</h2>
<p>The Helium Network is a collection of decentralized wireless networks, including IoT and mobile coverage. For IoT, Helium documents a LoRaWAN-based network where hotspot operators provide wireless coverage for devices. By deploying strategically placed antennas, hotspot operators can earn rewards through:</p>
<ul>
<li><strong>Proof of Coverage</strong>: Demonstrating wireless coverage to nearby hotspots</li>
<li><strong>Data Credits</strong>: Routing IoT device data through the network</li>
<li><strong>Network participation</strong>: Keeping deployed coverage online and useful</li>
</ul>
<p>Blockchain activity and Helium tooling exposed quantitative feedback about signal strength, witness relationships, and coverage across different connections, which made it possible to measure radio engineering progress instead of guessing.</p>
<h2>What antenna configuration worked in Arizona?</h2>
<img alt="Weatherproof equipment enclosure showing Helium hotspot and RF components" loading="lazy" width="1350" height="1800" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>Protecting sensitive electronics required custom weatherproof enclosures:</p>
<ul>
<li><strong>NEMA-rated enclosures</strong>: IP65+ protection against dust and moisture</li>
<li><strong>Thermal management</strong>: thermal paint and passive cooling for Arizona's extreme heat</li>
<li><strong>Hardware</strong>: Custom Built Antennas, Coax Cable, Splitters, Amplifiers, Tower, etc.</li>
</ul>
<img alt="Interior view of antenna control box with organized cable management" loading="lazy" width="1800" height="1350" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>We analyzed various antenna configurations to optimize signal propagation across Arizona's diverse terrain. The key factors we considered:</p>
<h3>Site Survey and Planning</h3>
<p>Each antenna deployment began with comprehensive site surveys:</p>
<ol start="0">
<li><strong>Azimuth and Elevation optimization</strong>: Ensuring optimal coverage and witness distances</li>
<li><strong>RF propagation modeling</strong>: Using terrain data and propagation software</li>
<li><strong>Existing hotspot analysis</strong>: Identifying practical witness distances from first-party deployment data</li>
<li><strong>Interference assessment</strong>: Measuring background RF noise levels</li>
<li><strong>Line-of-sight verification</strong>: Ensuring clear paths to target areas</li>
<li><strong>Hardware Quality</strong>: Coax cable, antenna, and other hardware efficiency and reliability</li>
</ol>
<h3>Frequency Characteristics</h3>
<p>Helium IoT uses LoRaWAN regional frequency plans; in the United States, deployments use the US915 region. That made antenna tuning and cable quality central to the build. We tested multiple antenna types:</p>
<ul>
<li><strong>Omnidirectional antennas</strong>: 360-degree coverage for urban deployments</li>
<li><strong>Directional Yagi antennas</strong>: Focused beam for long-distance links</li>
<li><strong>Collinear arrays</strong>: Enhanced gain while maintaining omnidirectional pattern</li>
</ul>
<img alt="Professional rooftop antenna installation with proper grounding and weatherproofing" loading="lazy" width="1350" height="1800" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<h3>Antenna Gain and Pattern Analysis</h3>
<p>Higher gain antennas concentrate RF energy, extending range but reducing vertical coverage. We measured the trade-offs:</p>
<pre><code>Low Gain (3 dBi):  Wider vertical pattern, shorter range
Medium Gain (6 dBi): Balanced coverage for suburban areas
High Gain (9+ dBi): Long-range links, narrow vertical pattern
</code></pre>
<h2>What I learned from field deployments</h2>
<p><strong>Height beat most desk theories.</strong> Moving an antenna from an indoor shelf to a roof changed the link budget more than many component swaps. The lesson was not "always buy more gain"; it was "improve the RF path first."</p>
<p><strong>Cable and enclosure work were product work.</strong> A high-gain antenna did not matter if the cable run was lossy, the enclosure overheated, or water intrusion took the hotspot offline.</p>
<p><strong>Helium made RF measurable enough to iterate.</strong> Witness data, coverage changes, and reward behavior gave us feedback loops. The numbers were noisy, but they were better than installing hardware and hoping.</p>
<p><strong>Local terrain mattered.</strong> Arizona rooftops, heat, UV exposure, and open desert paths created different constraints than a generic indoor hotspot guide. The useful decisions came from repeated field work, not one static parts list.</p>
<h2>Installation Challenges and Solutions</h2>
<h3>Rooftop Deployments</h3>
<img alt="Helium antenna against clear Arizona sky, showcasing optimal positioning" loading="lazy" width="1350" height="1800" decoding="async" data-nimg="1" class="my-8 w-full rounded-lg shadow-md" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=640&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=750&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=828&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=1080&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=1200&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.jpg&amp;w=1920&amp;q=85&amp;dpl=dpl_Bh6iraonHx1dvzqfRMLEAayCYbGA">
<p>Rooftop installations presented unique challenges in Arizona's extreme weather
conditions:</p>
<ul>
<li><strong>Wind loading</strong>: Securing antennas against monsoon winds up to 70+ mph</li>
<li><strong>Temperature cycling</strong>: Managing expansion/contraction from 20°F to 120°F+ swings</li>
<li><strong>UV degradation</strong>: Selecting materials resistant to intense desert sun</li>
<li><strong>Lightning protection</strong>: Implementing proper grounding systems</li>
</ul>]]></content:encoded>
            <author>dylan@starmorph.com (Dylan Boudro)</author>
        </item>
    </channel>
</rss>