← Examples
countries · Delaunay · low-poly · art

Crystalline Earth

The continents, crystallised. Points are scattered at random across the land and joined by Delaunay triangulation into a fine mesh; the long edges that would bridge the oceans are cut away, so each landmass sets into its own faceted lattice, glowing in the colour of its continent. It's generative — every load grows a slightly different crystal.

API Call

https://api.mapjson.com/v1/geo?layer=countries&filter=world&detail=medium&properties=continent

Code

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/topojson-client@3/dist/topojson-client.min.js"></script>
  <style>
    body { margin: 0; background: #06080e; }
    svg { width: 100%; height: auto; display: block; }
  </style>
</head>
<body>
  <svg id="map"></svg>
  <script>
  const W = 1100, H = 620;
  const svg = d3.select("#map").attr("viewBox", `0 0 ${W} ${H}`);
  const projection = d3.geoNaturalEarth1().rotate([-11, 0]);
  const CC = {"Africa":"#ffb43a","Asia":"#ff5a6e","Europe":"#58a6ff","North America":"#4be08a","South America":"#c07bff","Oceania":"#2fe0d4","Antarctica":"#cfd8e3"};
  d3.json("https://api.mapjson.com/v1/geo?layer=countries&filter=world&detail=medium&properties=continent&format=geojson").then(fc=>{
    projection.fitExtent([[16,16],[W-16,H-16]], fc);
    // paint continents to a hidden canvas — the land mask + the colour of each facet
    const cvn = document.createElement("canvas"); cvn.width = W; cvn.height = H;
    const ctx = cvn.getContext("2d"); const gp = d3.geoPath(projection, ctx);
    fc.features.forEach(f=>{ ctx.fillStyle = CC[f.properties.continent] || "#8a8f99"; ctx.beginPath(); gp(f); ctx.fill(); });
    const px = ctx.getImageData(0,0,W,H).data;
    const at = (x,y)=>{ if(x<0||y<0||x>=W||y>=H) return null; const i=((y|0)*W+(x|0))*4; return px[i+3]>10 ? [px[i],px[i+1],px[i+2]] : null; };
    // scatter points over the land
    const G = 12, pts = [];
    for (let y=0;y<H;y+=G) for (let x=0;x<W;x+=G){ const jx=x+(Math.random()-0.5)*G, jy=y+(Math.random()-0.5)*G; if (at(jx,jy)) pts.push([jx,jy]); }
    const del = d3.Delaunay.from(pts), tri = del.triangles, L2 = (G*2.6)**2;
    const g = svg.append("g").style("mix-blend-mode","screen");
    const seen = new Set();
    for (let i=0;i<tri.length;i+=3){
      for (let e=0;e<3;e++){
        const a=tri[i+e], b=tri[i+(e+1)%3], key = a<b ? a*1e6+b : b*1e6+a;
        if (seen.has(key)) continue; seen.add(key);
        const pa=pts[a], pb=pts[b], dx=pa[0]-pb[0], dy=pa[1]-pb[1];
        if (dx*dx+dy*dy > L2) continue;                                   // drop long ocean-spanning edges
        const c = at((pa[0]+pb[0])/2,(pa[1]+pb[1])/2) || [110,130,160];
        g.append("line").attr("x1",pa[0]).attr("y1",pa[1]).attr("x2",pb[0]).attr("y2",pb[1])
          .attr("stroke",`rgb(${c[0]},${c[1]},${c[2]})`).attr("stroke-width",0.5).attr("stroke-opacity",0.5);
      }
    }
    g.append("g").selectAll("circle").data(pts).join("circle")
      .attr("cx",d=>d[0]).attr("cy",d=>d[1]).attr("r",0.7).attr("fill","#eaf2ff").attr("fill-opacity",0.55);
  });
  </script>
</body>
</html>