<?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>Sun, 05 Apr 2026 16:46:28 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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Farchitecting-a-text-to-image-inference-platform%2Fpixelmuse-home.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<p><a href="https://pixelmuse.studio">PixelMuse</a> is a modern, type-safe text-to-image generation platform built with Next.js 14, Clerk Auth, and the Replicate API. Here's how I am designing and developing the architecture to create a seamless user experience with beautiful UI and a comprehensive + realiable AI image generation service.</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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<h2>Generating 3d Objects <code>.GBL</code> files</h2>
<p>Generated on PixelMuse using the <code>Trelis</code> AI model.</p>
<img alt="PixelMuse Gallery - Showcase of AI-generated images" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="rounded-lg shadow-md my-8 w-full" style="color:transparent" src="/articles/architecting-a-text-to-image-inference-platform/fireaxe.gif">
<h2>Technical Implementation Details</h2>
<h2>Application Architecture</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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<h2>TypeSafe API for Image Generation</h2>
<p>The integration with Replicate's API is handled through a type-safe client, generated from their OpenAPI specification:</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, leveraging its powerful features like React Server Components (RSC) and server actions. The entire application is built with TypeScript, ensuring type safety from the database schema to the API endpoints.</p>
<p>Key architectural decisions included:</p>
<ul>
<li>Using pnpm for faster, more efficient package management</li>
<li>Implementing a monorepo structure for better code organization</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 a robust, type-safe authentication system. We Sync User data from Clerk to our supabase via Drizzle and a webhook when a user signs up.</p>
<h2>Database and Type Safety</h2>
<p>The database layer is built on <code>Supabase</code> with <code>Drizzle ORM</code>, providing:</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>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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fmermaid-editor-ui.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<p><a href="https://mermaideditor.io">MermaidEditor.io</a> is a free online diagram editor for <a href="https://mermaid.js.org/">Mermaid.js</a> — the text-based diagramming syntax that GitHub renders natively in Markdown. I built it because GitHub gives you zero feedback when your Mermaid syntax is wrong: you commit, see a blank gray box, and have no idea what broke.</p>
<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.</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>The Problem: GitHub's Silent Failure</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>Architecture Overview</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>
<h2>The Export Pipeline: The Hardest Problem</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>AI Features: Repair Over 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>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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fpost-export-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-modal.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-upgrade-banner.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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="rounded-lg shadow-md my-8 w-full" 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_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fbuilding-mermaid-editor%2Fab-test-nav-layout.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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>Tech Decisions I'd Make 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> or <a href="https://cal.com/dylanboudro/intro">schedule a call</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="800" height="600" decoding="async" data-nimg="1" class="rounded-lg shadow-md my-8 w-full" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=640&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-thumbnail.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<h2>How and why 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, 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 to be intriguing that a picture could be such a powerful object that it was worth keeping past all other possessions. It made me think, what should the focus of my meditation be? What do I want to become in greater union and understanding with? 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 was utilizing radio waves as a proof of consensus.</p>
<p>I purchased a helium miner. On the first few days I set it up in 2021 it was earning a noteable amount of USD with the stock antenna. I decided I needed to upgrade my antenna to improve my earnings. So I googled radio shop, and drove to one of the locations that showed up on Google. I was expecting a Radio Shack, however 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. In 2021, I wasn't expecting much of a response for this out of the ordinary request, however the shop owner was clearly a curious engineer and found it fascinating. He offered to let me set the miner up on his roof, explaining that the height of the antenna is of key importance for maximizing the signal distance of the antenna. This was the first of 50+ times I ended up spending working with the owner of the CB shop in brainstorming, designing, installing, managing, upgrading and fixing Helium Antennas.'</p>
<h2>The Helium Network: Decentralized IoT Infrastructure</h2>
<p>The Helium Network is a collection of decentralized wireless networks (5G, WIFI, LoRa IoT, etc.) that rewards users with HNT (Helium Network Token) cryptocurrency for providing network infrastructure and coverage. By deploying strategically placed antennas, miners 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 Consensus</strong>: Participating in blockchain validation</li>
</ul>
<p>the blockchain transactions on Helium provide quantitative data on the antennas signal strength and coverage across different connections, allowing for measurement of radio engineering progress.</p>
<h3>Our Antenna Configuration</h3>
<img alt="Weatherproof equipment enclosure showing Helium hotspot and RF components" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="rounded-lg shadow-md my-8 w-full" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=640&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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 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="800" height="600" decoding="async" data-nimg="1" class="rounded-lg shadow-md my-8 w-full" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=640&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-box-inside-landscape.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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 optimal witness distances (300m-80km)</li>
<li><strong>Interference assessment</strong>: Measuring background RF noise levels</li>
<li><strong>Line-of-sight verification</strong>: Ensuring clear paths to target areasy</li>
<li><strong>Hardware Quality</strong>: Coax cable, antenna, and other hardware efficiency and reliability</li>
</ol>
<h3>Frequency Characteristics</h3>
<p>Helium operates on the 915 MHz ISM band in North America, requiring antennas specifically tuned for this frequency range. 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="800" height="600" decoding="async" data-nimg="1" class="rounded-lg shadow-md my-8 w-full" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=640&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-roof.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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>Installation Challenges and Solutions</h2>
<h3>Rooftop Deployments</h3>
<img alt="Helium antenna against clear Arizona sky, showcasing optimal positioning" loading="lazy" width="800" height="600" decoding="async" data-nimg="1" class="rounded-lg shadow-md my-8 w-full" style="color:transparent" sizes="(min-width: 1024px) 800px, 100vw" srcset="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=640&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 640w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=750&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 750w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=828&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 828w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=1080&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1080w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=1200&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1200w, /_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ 1920w" src="/_next/image?url=%2Farticles%2Fradio-engineering-helium-antenna%2Fantenna-sky.png&amp;w=1920&amp;q=85&amp;dpl=dpl_6gMaRoHYJy9btptwzYP9ZoxntdEQ">
<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>