Back to Blog

How to Render an Interactive Human Design Chart with SVG and JavaScript

Map any humandesignapi.nl v2 response to a visual chart in ~60 lines of vanilla JS. No framework required. Includes a downloadable SVG.

Posted by

What you'll build

Rendering an interactive Human Design chart takes two ingredients: an SVG file with predictable element IDs, and JavaScript functions that toggle CSS classes on those IDs based on the API response. This guide gives you a downloadable SVG with 64 gate elements (each split into design and personality halves) and a ~60-line vanilla-JS renderer that takes any POST /v2/charts/coordinates response and colors activated gates in design red (#8b3000) vs personality espresso (#2d1400), with defined centers highlighted in light brown. No framework required.


The finished result is a static HTML page where design and personality activations are color-coded the moment the chart loads. A follow-up guide will wrap this in a React component for Next.js App Router. For now, vanilla JS keeps the concept clear.

See CodePen example

Prerequisites

You need two things before writing any code:

  • The chart SVG: download it at /blog/render-human-design-chart-svg-javascript/chart.svg. It uses a viewBox="0 0 852 1310", and contains 192 gate-path elements (64 gates × 3: fill halves -a/-b + stroke outline) and 9 center group IDs.
  • A humandesignapi.nl API key: the Developer plan at €35/month gives access to POST /v2/charts/coordinates, which returns the activations object this renderer reads. The Hobbyist plan's /v2/charts/simple endpoint does not include activations.

The ID scheme: the contract between API and SVG

The renderer works because the SVG element IDs follow a predictable pattern that maps directly to what the API returns.


9 center IDs

Each of the nine centers is a group element with one of these IDs: Head, Ajna, Throat, G, Ego, Sacral, Solar_Plexus, Spleen, and Root. The API's data.centers array returns these same strings, except that "Solar Plexus" (with a space) maps to the SVG ID Solar_Plexus (with an underscore). The renderer handles this with a single replace(/ /g, "_") call.


Gate IDs: three elements per gate

Each of the 64 gates is represented by three <path> elements:

  • Gate{N}-a, fill only, no stroke. Receives hd-design class when gate N is activated on the design (unconscious) side.
  • Gate{N}-b, fill only, no stroke. Receives hd-personality class when activated on the personality (conscious) side.
  • Gate{N}-outline, stroke only, no fill. Draws the gate border and is never touched by JavaScript.

When a gate is active on both sides, -a shows design red and -b shows personality espresso, two half-parallelograms in different colors. Your JS only needs to query -a and -b; the outline element is purely decorative.

The API response: what we're mapping from

The renderer consumes two top-level fields from the data object:

  • data.centers, an array of strings, one per defined center, e.g. ["Ajna", "Throat", "Ego", "Sacral"]. Empty if the chart has no defined centers.
  • data.activations, an object with two keys, design and personality. Each maps planet names to "gate.line" strings. e.g. { sun: "27.4", earth: "28.4", ... }. To get a gate number, take the integer part: parseInt("27.4", 10) → 27.

Each planet activates exactly one gate, so a chart has up to 13 design gates and 13 personality gates. Gates activated on both sides are where the split-color display matters most. The concept-to-API guide covers the full response shape including what each planet activation means within the Human Design system.

Step 1: Inline the SVG

You cannot style SVG elements with JavaScript if the SVG is loaded via an <img src="chart.svg"> tag that treats the file as an opaque image with no accessible DOM. The SVG must be inlined directly in your HTML so document.querySelector can reach individual elements.


Open the downloaded SVG, copy its contents, and paste it where you want the chart to appear:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Human Design Chart</title>
  <link rel="stylesheet" href="chart.css">
</head>
<body>
  <div id="chart-container">
    <!-- paste the entire SVG here -->
    <svg id="chart" viewBox="0 0 852 1310" xmlns="http://www.w3.org/2000/svg">
      ...
    </svg>
  </div>
  <script src="chart.js"></script>
</body>
</html>

The viewBox keeps the chart responsive. Set the container width in CSS and the SVG scales to fit.

Step 2: Write the highlight functions

Two functions do the work: applyGates extracts gate numbers from the activations object and toggles color classes on each gate's fill halves; applyCenters marks defined center groups.

// chart.js

// "27.4" → 27  (planet string to gate number)
function getActivatedGates(activations) {
  const design = new Set();
  const personality = new Set();
  Object.values(activations.design).forEach(v =>
    design.add(parseInt(v.split(".")[0], 10))
  );
  Object.values(activations.personality).forEach(v =>
    personality.add(parseInt(v.split(".")[0], 10))
  );
  return { design, personality };
}

function applyGates(svgRoot, activations) {
  const { design, personality } = getActivatedGates(activations);
  for (let g = 1; g <= 64; g++) {
    const a = svgRoot.querySelector(`#Gate${g}-a`);
    const b = svgRoot.querySelector(`#Gate${g}-b`);
    if (!a || !b) continue; // gate not in this SVG
    const inD = design.has(g);
    const inP = personality.has(g);
    // Design-only or both → -a is design red
    a.classList.toggle("hd-design",      inD);
    a.classList.toggle("hd-personality", !inD && inP);
    // Personality-only or both → -b is personality espresso
    b.classList.toggle("hd-design",      inD && !inP);
    b.classList.toggle("hd-personality", inP);
  }
}

function applyCenters(svgRoot, centers) {
  centers.forEach(name => {
    const id = name.replace(/ /g, "_"); // "Solar Plexus" → "Solar_Plexus"
    svgRoot.querySelector(`#${id}`)?.classList.add("hd-center-defined");
  });
}

// Call once the API response is available
const svgRoot = document.getElementById("chart");
applyGates(svgRoot, apiResponse.data.activations);
applyCenters(svgRoot, apiResponse.data.centers);

When a gate is activated on both sides, -a shows design red and -b shows personality espresso. Two half-parallelograms rendered side by side. When only one side is active, both halves share the same color class, producing the appearance of a single solid bar.

Step 3: The CSS

Three rules cover all states. Gate half-elements carry fill only (no stroke), so CSS only needs to set fill. Use !important to override the default SVG fill attribute.

/* chart.css */
.hd-design {
  fill: #8b3000 !important; /* dark rust; design (unconscious) */
}
.hd-personality {
  fill: #2d1400 !important; /* espresso; personality (conscious) */
}
.hd-center-defined > path:first-child {
  fill: #b59181 !important; /* light brown; defined center */
}

/* Optional: animate the highlights drawing in */
svg * {
  transition: fill 0.4s ease;
}

The transition rule applies to every SVG child, so highlights fade in smoothly when applyGates and applyCenters are called. If you add @media (prefers-reduced-motion: no-preference), wrap the transition inside it so the animation only plays for users who have not requested reduced motion.

Step 4: Wire to the API

Never put your API key in client-side JavaScript. It will be visible in the browser's network tab and source. Make the API call from your server (a Node.js handler, a Cloudflare Worker, a Next.js Server Action) and return the data object to the browser. Here is the server-side fetch:

// server.js (Node / Express example)
const response = await fetch(
  "https://api.humandesignapi.nl/v2/charts/coordinates",
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.HD_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      birthdate: "1990-06-15",
      birthtime: "12:30",
      latitude: 52.3676,
      longitude: 4.9041,
    }),
  }
);
const { data } = await response.json();
// Pass data to the client as JSON

On the client, receive the JSON and call both functions:

// chart.js (browser)
const res = await fetch("/api/chart", { method: "POST", body: JSON.stringify(formData) });
const data = await res.json();
const svgRoot = document.getElementById("chart");
applyGates(svgRoot, data.activations);
applyCenters(svgRoot, data.centers);

The quickstart guide and the authentication reference cover token setup in detail. Rate limit is 100 requests per minute per API key; chart data for a given birth input is deterministic, so cache aggressively on the server side.

Going further

Once the basic renderer is working, three improvements are worth adding:

  • Hover tooltips: add a mouseover listener to each activated gate element and display the gate number and its channel from data.channels (e.g. "The Channel of Power (34-57)") in a floating tooltip.
  • Responsive scaling: set width: 100% on the container and let the SVG viewBox do the work. Test at 320 px viewport width; the chart is tall and narrow, so it scales well on mobile without any extra handling.
  • Staggered animation: apply color classes to each Gate{N}-a and Gate{N}-b element with an incrementing setTimeout delay to draw the chart gate-by-gate rather than all at once.

Production checklist

  • ARIA label: add aria-label="Human Design chart" and role="img" to the <svg> element so screen readers announce it correctly.
  • Reduced motion: wrap the CSS transition in @media (prefers-reduced-motion: no-preference) so animations only play for users who haven't requested reduced motion.
  • SSR pre-render: if you know the chart data at render time (e.g. in a Next.js Server Component), add the hd-design / hd-personality / hd-center-defined classes server-side by injecting them into the SVG string before sending HTML. This avoids a flash of unhighlighted content on first load.
  • SVG size: the SVG viewBox is 852 × 1310. Inline SVG does not add to page weight beyond the HTML itself; no separate image request is made.

Download the SVG and start building

The chart SVG, with 64 gates (design/personality halves) and 9 center group IDs, stable and documented, is available at /blog/render-human-design-chart-svg-javascript/chart.svg. Pair it with a Developer plan (€35/month for 10,000 charts) and you have everything you need to ship a working chart renderer today.


For the full API response shape; all 22 fields including activations, incarnation cross, and variables: see the v2 API reference. The concept-to-API guide maps every Human Design concept to its exact JSON field.


Background on the system: Human Design System (Wikipedia). SVG DOM manipulation reference: Element.classList (MDN), Inline SVG (MDN).