← Examples
rivers · lakes · coastlines · physical · minimal

Earth's Rivers

Every river and lake on the planet, drawn from the rivers, lakes and coastlines layers — no borders, no labels. A faint coastline grounds the glowing river network so the continents read as their own circulatory system. Three layer calls, composed on a Natural Earth projection.

rivers
lakes
coastline

API Calls

https://api.mapjson.com/v1/geo?layer=coastlines&detail=medium
https://api.mapjson.com/v1/geo?layer=rivers&detail=medium
https://api.mapjson.com/v1/geo?layer=lakes&detail=medium

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: #05080f; }
    svg { width: 100%; height: 100vh; display: block; }
  </style>
</head>
<body>
  <svg id="map"></svg>
  <script>
  const W = 1400, H = 760;
  const svg = d3.select("#map").attr("viewBox", `0 0 ${W} ${H}`);
  const defs = svg.append("defs");
  const fl = defs.append("filter").attr("id", "glow").attr("x", "-20%").attr("y", "-20%").attr("width", "140%").attr("height", "140%");
  fl.append("feGaussianBlur").attr("stdDeviation", "1.3").attr("result", "b");
  const mg = fl.append("feMerge"); mg.append("feMergeNode").attr("in", "b"); mg.append("feMergeNode").attr("in", "SourceGraphic");

  const projection = d3.geoNaturalEarth1().rotate([-11, 0]);
  const path = d3.geoPath(projection);
  const API = "https://api.mapjson.com/v1/geo?format=topojson&";
  const feat = t => topojson.feature(t, t.objects.geo);

  Promise.all([
    d3.json(API + "layer=coastlines&detail=medium"),
    d3.json(API + "layer=rivers&detail=medium"),
    d3.json(API + "layer=lakes&detail=medium"),
  ]).then(([coast, rivers, lakes]) => {
    const c = feat(coast), r = feat(rivers), l = feat(lakes);
    projection.fitExtent([[10, 10], [W - 10, H - 10]], c);
    // faint coastline, barely there
    svg.append("g").selectAll("path").data(c.features).join("path")
      .attr("d", path).attr("fill", "none").attr("stroke", "#2c4d70").attr("stroke-width", 0.5).attr("stroke-linejoin", "round");
    // lakes as glowing pools
    svg.append("g").attr("filter", "url(#glow)").selectAll("path").data(l.features).join("path")
      .attr("d", path).attr("fill", "#1f8fd0").attr("fill-opacity", 0.75);
    // rivers as veins
    svg.append("g").attr("filter", "url(#glow)").selectAll("path").data(r.features).join("path")
      .attr("d", path).attr("fill", "none").attr("stroke", "#40cfff").attr("stroke-width", 0.55)
      .attr("stroke-opacity", 0.9).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
  });
  </script>
</body>
</html>