World countries rendered on an orthographic projection — the view from space. Land in sage green, ocean in slate blue, 30° graticule in white. Drag to rotate.
https://api.mapjson.com/v1/geo?layer=countries&detail=low
<!DOCTYPE html>
<html>
<head>
<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 { background: #1a2a3a; margin: 0; }
svg { width: 100%; display: block; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const width = 800, height = 800;
const svg = d3.select("#map").append("svg").attr("viewBox", `0 0 ${width} ${height}`);
const projection = d3.geoOrthographic()
.scale(width / 2 - 20)
.translate([width / 2, height / 2])
.clipAngle(90)
.rotate([0, -20]);
const path = d3.geoPath().projection(projection);
const graticule = d3.geoGraticule().step([30, 30])();
// Ocean fill
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", projection.scale())
.attr("fill", "#3a6a8a");
// Graticule
const graticulePath = svg.append("path")
.datum(graticule)
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 0.3)
.attr("stroke-opacity", 0.3)
.attr("d", path);
// Countries (land)
let landPath;
const url = "https://api.mapjson.com/v1/geo?layer=countries&detail=low";
d3.json(url).then(topo => {
const countries = topojson.feature(topo, topo.objects.geo);
landPath = svg.append("path")
.datum(countries)
.attr("fill", "#8a9e7a")
.attr("stroke", "#3a6a8a")
.attr("stroke-width", 0.4)
.attr("d", path);
});
// Globe outline
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", projection.scale())
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-width", 0.5)
.attr("stroke-opacity", 0.3);
// Drag to rotate
let lambda = 0, phi = -20;
svg.call(d3.drag().on("drag", function(event) {
lambda += event.dx * 0.4;
phi = Math.max(-90, Math.min(90, phi - event.dy * 0.4));
projection.rotate([lambda, phi]);
graticulePath.attr("d", path);
if (landPath) landPath.attr("d", path);
})).style("cursor", "grab");
</script>
</body>
</html>