<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 冠</title>
    <description>The latest articles on DEV Community by 冠 (@_eb3468ba11d30aaf81a0e85).</description>
    <link>https://dev.to/_eb3468ba11d30aaf81a0e85</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3454222%2F6cb9b375-4dde-456b-9d3e-1216f849a798.png</url>
      <title>DEV Community: 冠</title>
      <link>https://dev.to/_eb3468ba11d30aaf81a0e85</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_eb3468ba11d30aaf81a0e85"/>
    <language>en</language>
    <item>
      <title>I Built an AI Landscape Design Tool with Next.js + Cloudflare</title>
      <dc:creator>冠</dc:creator>
      <pubDate>Sun, 02 Aug 2026 01:16:09 +0000</pubDate>
      <link>https://dev.to/_eb3468ba11d30aaf81a0e85/i-built-an-ai-landscape-design-tool-with-nextjs-cloudflare-1oo0</link>
      <guid>https://dev.to/_eb3468ba11d30aaf81a0e85/i-built-an-ai-landscape-design-tool-with-nextjs-cloudflare-1oo0</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foh7qi4adr6tgos9l3ofk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foh7qi4adr6tgos9l3ofk.png" alt=" " width="799" height="418"&gt;&lt;/a&gt;&lt;br&gt;
I wanted a practical answer to one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can I preview a yard makeover from a real photo before spending money?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Generic &lt;a href="https://landscapedesignai.net" rel="noopener noreferrer"&gt;AI image tools&lt;/a&gt; make pretty gardens. They are worse at keeping your actual house, fence, and proportions. So I built &lt;strong&gt;LandscapeAI&lt;/strong&gt; — upload one outdoor photo, pick a style, generate a design concept.&lt;/p&gt;

&lt;p&gt;Live app: &lt;a href="https://landscapedesignai.net/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=landscapeai_short" rel="noopener noreferrer"&gt;landscapedesignai.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a short build log: stack, architecture, and the bugs that mattered.&lt;/p&gt;
&lt;h2&gt;
  
  
  The product
&lt;/h2&gt;

&lt;p&gt;Users upload a front yard, backyard, garden, patio, or balcony photo. They choose a style (modern, cottage, zen, Mediterranean, xeriscape…), add optional goals like privacy or low maintenance, then generate a concept.&lt;/p&gt;

&lt;p&gt;New users get a small free starter allowance. Extra runs use credits sold as one-time packs.&lt;/p&gt;

&lt;p&gt;Clear boundary helped a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good for early visualization and style comparison&lt;/li&gt;
&lt;li&gt;Not construction drawings, permits, or guaranteed plant lists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Narrow scope made the engineering shippable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App&lt;/td&gt;
&lt;td&gt;Next.js 15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy&lt;/td&gt;
&lt;td&gt;OpenNext + Cloudflare Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB / Storage&lt;/td&gt;
&lt;td&gt;D1 + R2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;NextAuth (Google)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payments&lt;/td&gt;
&lt;td&gt;Creem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image gen&lt;/td&gt;
&lt;td&gt;kie → fal → Replicate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cloudflare end-to-end kept infra simple for an early AI SaaS: compute, database, storage, DNS in one place.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Everything goes through one gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload photo + prompt
  → auth + credit check
  → content moderation (fail closed)
  → provider router (kie / fal / replicate)
  → save result to R2
  → decrease credits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One route for generation means credits, safety, failover, and logging stay in one place. I recommend this for any AI image product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three lessons from production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Multi-provider fallback is worth it
&lt;/h3&gt;

&lt;p&gt;AI image APIs fail, rate-limit, or run dry. A tiny router saved the product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;primary: kie&lt;/li&gt;
&lt;li&gt;fallback: fal&lt;/li&gt;
&lt;li&gt;third: Replicate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users care that Generate works, not which vendor won.&lt;/p&gt;

&lt;p&gt;Also: some models reject data URLs for reference images. They want public URLs. I upload the user photo first, then pass the returned URL into generation. If you are doing photo-to-image editing, assume that early.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Credits beat “unlimited”
&lt;/h3&gt;

&lt;p&gt;Image generation has real unit cost. LandscapeAI uses starter credits + one-time packs instead of forcing subscriptions first.&lt;/p&gt;

&lt;p&gt;Yard redesign is often a short project. People try a few styles, then stop. One-time packs match that better than a monthly plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI payments need compliance, not just checkout
&lt;/h3&gt;

&lt;p&gt;For AI image products, payment providers may require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visible paid pricing before purchase&lt;/li&gt;
&lt;li&gt;consistent support email&lt;/li&gt;
&lt;li&gt;explicit NSFW prohibition in Terms / AUP&lt;/li&gt;
&lt;li&gt;moderation on every prompt before generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My rule: &lt;strong&gt;no prompt reaches the model without a moderation decision&lt;/strong&gt;. Block on &lt;code&gt;deny&lt;/code&gt;/&lt;code&gt;flag&lt;/code&gt;, and fail closed if moderation is down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that looked like “AI is broken”
&lt;/h2&gt;

&lt;p&gt;One production error was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload failed: 400 Bad Request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generation had already succeeded. R2 upload failed because production storage config/secrets were wrong. From the user’s view, the whole feature was dead.&lt;/p&gt;

&lt;p&gt;For AI apps, success means all three:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;model success&lt;/li&gt;
&lt;li&gt;storage success&lt;/li&gt;
&lt;li&gt;credit ledger success&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Log them separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’d do earlier next time
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Build the generation gateway first (credits + moderation + providers)&lt;/li&gt;
&lt;li&gt;Treat storage secrets as launch-critical, not optional&lt;/li&gt;
&lt;li&gt;Show pricing packs before payments are fully polished&lt;/li&gt;
&lt;li&gt;Log moderation success, not only failures&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://landscapedesignai.net/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=landscapeai_short" rel="noopener noreferrer"&gt;Generate an AI landscape design from your yard photo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sign in, upload one outdoor photo, and use the free starter designs.&lt;/p&gt;

&lt;p&gt;If you are building a similar AI SaaS, comment with your stack — happy to compare notes on provider fallback, Cloudflare deploys, or credit packaging.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>As a fellow tab hoarder, I use tools to finish projects so I can finally close them! I create graphics with https://graffiti-generator.org, edit images via https://fireredimage.org, and make academic figures with https://paper-banana.app. For a fun break,</title>
      <dc:creator>冠</dc:creator>
      <pubDate>Wed, 15 Apr 2026 09:16:30 +0000</pubDate>
      <link>https://dev.to/_eb3468ba11d30aaf81a0e85/as-a-fellow-tab-hoarder-i-use-tools-to-finish-projects-so-i-can-finally-close-them-i-create-beb</link>
      <guid>https://dev.to/_eb3468ba11d30aaf81a0e85/as-a-fellow-tab-hoarder-i-use-tools-to-finish-projects-so-i-can-finally-close-them-i-create-beb</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://graffiti-generator.org" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;graffiti-generator.org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://fireredimage.org" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;fireredimage.org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://paper-banana.app" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;paper-banana.app&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>I Built a Free Astrocartography Calculator with Next.js — Here's the Full Story</title>
      <dc:creator>冠</dc:creator>
      <pubDate>Sat, 28 Mar 2026 02:47:58 +0000</pubDate>
      <link>https://dev.to/_eb3468ba11d30aaf81a0e85/i-built-a-free-astrocartography-calculator-with-nextjs-heres-the-full-story-4fbd</link>
      <guid>https://dev.to/_eb3468ba11d30aaf81a0e85/i-built-a-free-astrocartography-calculator-with-nextjs-heres-the-full-story-4fbd</guid>
      <description>&lt;p&gt;Most astrocartography tools online are either locked behind a $20/month paywall, built on Flash-era technology, or so cluttered with ads you can barely find the actual calculator. As a developer who stumbled into the world of astrology charts, I kept thinking: &lt;em&gt;someone should just build a clean, free one.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I did. Here's the honest story of how I built &lt;strong&gt;&lt;a href="https://astrocarto.org" rel="noopener noreferrer"&gt;Astrocarto&lt;/a&gt;&lt;/strong&gt; — the technical decisions, the unexpected hard parts, and what I'd do differently.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  What Even Is Astrocartography?
&lt;/h2&gt;

&lt;p&gt;Quick background for the non-astrology crowd (which is probably most of you):&lt;/p&gt;

&lt;p&gt;Astrocartography is a technique that takes the positions of planets at the exact moment you were born and projects them as lines onto a world map. The idea is that certain locations carry specific planetary energies for you — your "Venus line" might run through Paris, your "Mars line" through somewhere you'd rather avoid.&lt;/p&gt;

&lt;p&gt;Whether or not you believe any of that — the &lt;em&gt;math&lt;/em&gt; is genuinely fascinating. We're talking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculating precise planetary positions using historical ephemeris data&lt;/li&gt;
&lt;li&gt;Transforming between multiple astronomical coordinate systems&lt;/li&gt;
&lt;li&gt;Projecting those coordinates onto a 2D geographic map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a developer, I was hooked on the problem before I even thought about the product.&lt;/p&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Framework:        Next.js 14 (App Router)
Styling:          Tailwind CSS
Map Rendering:    Leaflet + React-Leaflet
Astro Calc:       astronomia (npm)
Deployment:       Vercel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll explain the key choices below.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Next.js?
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. SEO is everything for a niche tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People actively search for "free astrocartography calculator" — I needed server-side rendering so those pages could rank. With Next.js App Router, my calculator pages are fully rendered on the server before they hit the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Route Handlers for calculation logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn't want heavy astronomical math running on the client. Next.js Route Handlers let me keep that on the server cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/calculate/route.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nodejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// critical — don't use Edge here&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;birthDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;birthTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculateNatalChart&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;birthDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;birthTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Zero-config Vercel deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Push to main → live in 30 seconds. For an indie project, this matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hardest Part: Astronomical Calculations
&lt;/h2&gt;

&lt;p&gt;I expected the map to be the hard part. I was wrong. The calculations nearly broke me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the &lt;code&gt;astronomia&lt;/code&gt; library
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;astronomia&lt;/code&gt; is a JavaScript port of Jean Meeus's &lt;em&gt;Astronomical Algorithms&lt;/em&gt; — the gold standard reference for this kind of math. It handles the heavy lifting of planetary position calculations.&lt;/p&gt;

&lt;p&gt;Here's a simplified version of how I get a planet's ecliptic longitude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;solar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;julian&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astronomia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPlanetPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;birthDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;birthTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Convert birth datetime to Julian Day Number&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dateStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;birthDate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;T&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;birthTime&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:00Z`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;julian&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateToJD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dateStr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// Get Sun's position (as example)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sunPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apparentLongitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Convert from radians to degrees&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pmod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sunPos&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The coordinate system nightmare
&lt;/h3&gt;

&lt;p&gt;Where it got painful: astrocartography lines require converting between &lt;em&gt;three&lt;/em&gt; coordinate systems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ecliptic coordinates&lt;/strong&gt; — where planets are relative to Earth's orbital plane&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equatorial coordinates&lt;/strong&gt; — right ascension + declination (like lat/lng for space)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geographic coordinates&lt;/strong&gt; — actual latitude/longitude on Earth's surface&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For each planet, I need to find every latitude on Earth where that planet was on the Ascendant, Descendant, Midheaven, or IC at the moment of birth. That means iterating through latitudes and solving for the longitude where the condition holds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findAscendantLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;planet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Step through every latitude&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Binary search for the longitude where this planet rises&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;solveForAscendant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;planet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting this right took me about two weeks of head-scratching, cross-referencing with known charts, and fixing edge cases near the poles.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rendering the Map with Leaflet
&lt;/h2&gt;

&lt;p&gt;Once I had the coordinate arrays, rendering them with Leaflet was comparatively straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MapContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TileLayer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Polyline&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-leaflet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AstroMap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;planetLines&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MapContainer&lt;/span&gt;
      &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
      &lt;span class="nx"&gt;zoom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;600px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TileLayer&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;attribution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;© OpenStreetMap contributors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;planetLines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;planet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Polyline&lt;/span&gt;
          &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;planet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;positions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;pathOptions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MapContainer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One Leaflet gotcha with Next.js: Leaflet touches &lt;code&gt;window&lt;/code&gt; on import, which breaks SSR. Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Use dynamic import with ssr: false&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AstroMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/components/AstroMap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map-skeleton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What Astrocarto Can Do Right Now
&lt;/h2&gt;

&lt;p&gt;The tool currently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Rising Sign Calculator&lt;/strong&gt; — your ascendant based on exact birth time + location&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Natal Chart&lt;/strong&gt; — full planetary positions at birth&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Transit Chart&lt;/strong&gt; — current planetary positions overlaid on your natal chart&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Relocation Chart&lt;/strong&gt; — how your chart shifts if you move cities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All free. No account required. Just enter your birth details and go.&lt;/p&gt;

&lt;p&gt;Try it: &lt;strong&gt;&lt;a href="https://astrocarto.org" rel="noopener noreferrer"&gt;astrocarto.org&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Validate calculations against existing tools first&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I spent days debugging math that turned out to be correct — I just didn't have a reliable reference to check against. Next time I'd set up automated comparison tests against established chart software from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Build the map UI before perfecting the calculations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Seeing lines on a map, even approximate ones, keeps you motivated. I got too deep into calculation accuracy before I had anything visual to show for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Add caching earlier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Astronomical calculations for the same birth data always return the same result. I should have added a simple cache from the start rather than recalculating on every request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple in-memory cache I added later&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculateNatalChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;ul&gt;
&lt;li&gt;[ ] Mobile-optimized map gestures&lt;/li&gt;
&lt;li&gt;[ ] Shareable chart URLs (so you can send your chart to a friend)&lt;/li&gt;
&lt;li&gt;[ ] PDF export&lt;/li&gt;
&lt;li&gt;[ ] More celestial bodies — asteroids, Chiron, nodes&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Building niche tools is genuinely one of my favorite things to do as an indie developer. The intersection of an obscure domain and a real technical problem is where the most interesting projects live.&lt;/p&gt;

&lt;p&gt;If you've built something in a weird niche — astrology, birdwatching, chess, anything — I'd love to hear about it in the comments.&lt;/p&gt;

&lt;p&gt;And if you're curious about your own astrocartography: &lt;strong&gt;&lt;a href="https://astrocarto.org" rel="noopener noreferrer"&gt;astrocarto.org&lt;/a&gt;&lt;/strong&gt; — takes about 60 seconds.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>indiedev</category>
    </item>
    <item>
      <title>I Got Obsessed with LinkedIn Pinpoint, Until I Found This Tool</title>
      <dc:creator>冠</dc:creator>
      <pubDate>Tue, 21 Oct 2025 11:11:53 +0000</pubDate>
      <link>https://dev.to/_eb3468ba11d30aaf81a0e85/i-got-obsessed-with-linkedin-pinpoint-until-i-found-this-tool-17m2</link>
      <guid>https://dev.to/_eb3468ba11d30aaf81a0e85/i-got-obsessed-with-linkedin-pinpoint-until-i-found-this-tool-17m2</guid>
      <description>&lt;p&gt;Get today's LinkedIn Pinpoint answer instantly! &lt;br&gt;
&lt;a href="https://pinpointanswer.net" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;br&gt;
Updated daily with verified solutions, detailed clue explanations, and strategies to maintain your winning streak. Free access to current and historical puzzle answers.&lt;/p&gt;

</description>
      <category>pinpoint</category>
    </item>
    <item>
      <title>wplaceart</title>
      <dc:creator>冠</dc:creator>
      <pubDate>Sat, 23 Aug 2025 09:43:59 +0000</pubDate>
      <link>https://dev.to/_eb3468ba11d30aaf81a0e85/wplaceart-2f8o</link>
      <guid>https://dev.to/_eb3468ba11d30aaf81a0e85/wplaceart-2f8o</guid>
      <description>&lt;p&gt;Welcome to &lt;a href="https://wplaceart.com" rel="noopener noreferrer"&gt;Wplace Art&lt;/a&gt;, your premier destination for transforming ordinary images into extraordinary pixel art masterpieces. Our powerful Wplace Pixel converter combines cutting-edge technology with intuitive design, making it easy to create stunning retro-style artwork perfect for games, social media, and personal projects.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
