The map below shows a simplified globe with several CDN Points of Presence (PoPs). Click anywhere to drop a user — the demo will instantly highlight which PoP is closest and simulate the latency savings compared to fetching from a single origin server.
<!-- CDN anycast demo — click the map to place a user and find the nearest PoP -->
<div class="layout">
<div class="map-wrap">
<svg id="map" viewBox="0 0 500 280" xmlns="http://www.w3.org/2000/svg" aria-label="World map with CDN Points of Presence">
<!-- Ocean background -->
<rect width="500" height="280" fill="#e8f0f7" rx="8"/>
<!-- Simplified continent outlines -->
<!-- North America -->
<path d="M60,40 L140,35 L155,80 L130,120 L80,130 L55,100 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- South America -->
<path d="M105,145 L145,140 L155,190 L130,240 L95,235 L85,185 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Europe -->
<path d="M210,30 L255,28 L260,70 L235,80 L210,65 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Africa -->
<path d="M210,90 L255,85 L260,155 L235,200 L205,195 L195,145 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Asia -->
<path d="M270,25 L410,20 L415,110 L370,130 L300,125 L265,90 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Oceania -->
<path d="M355,160 L410,155 L415,205 L375,215 L345,200 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- PoP markers rendered by JS -->
<g id="pops"></g>
<!-- User click position -->
<g id="user-group"></g>
<!-- Connection lines drawn by JS -->
<g id="lines"></g>
</svg>
<p class="hint">Click anywhere on the map to place a user request</p>
</div>
<div class="panel">
<h3 class="panel-title">Routing Result</h3>
<div id="result" class="result-idle">Click the map to route a request.</div>
<div id="stats" class="stats hidden">
<div class="stat-row"><span class="stat-label">Nearest PoP</span><span id="nearest-name" class="stat-val"></span></div>
<div class="stat-row"><span class="stat-label">Edge latency</span><span id="edge-lat" class="stat-val"></span></div>
<div class="stat-row"><span class="stat-label">Origin latency</span><span id="origin-lat" class="stat-val"></span></div>
<div class="stat-row"><span class="stat-label">Latency saved</span><span id="saving" class="stat-val green"></span></div>
<div class="stat-row"><span class="stat-label">Cache hit rate</span><span id="cache-hit" class="stat-val"></span></div>
</div>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
</div>
/* Box-sizing reset and zero margins */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; color: #222; }
.layout { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
.map-wrap { flex: 1 1 300px; }
#map { width: 100%; height: auto; cursor: crosshair; border-radius: 8px;
border: 1px solid #c5d5e5; display: block; }
/* Small hint text below the map */
.hint { font-size: .8rem; color: #555; margin-top: 6px; text-align: center; }
/* Side panel card with routing results */
.panel { flex: 0 0 180px; background: #f0f5fa; border: 1px solid #c5d5e5;
border-radius: 8px; padding: 12px; display: flex; flex-direction: column; gap: 8px; }
.panel-title { font-size: .85rem; font-weight: 700; color: #1d3557; }
.result-idle { font-size: .8rem; color: #888; }
.stats { display: flex; flex-direction: column; gap: 6px; }
.hidden { display: none; }
.stat-row { display: flex; flex-direction: column; }
.stat-label { font-size: .7rem; color: #666; text-transform: uppercase; letter-spacing: .03em; }
.stat-val { font-size: .9rem; font-weight: 700; color: #1d3557; }
.stat-val.green { color: #0a7d33; }
/* Ghost button styling */
button.ghost { font: 600 13px system-ui; padding: .35rem .7rem; border: 1px solid #1d3557;
background: #fff; color: #1d3557; border-radius: 6px; cursor: pointer; margin-top: 4px; }
/* PoP marker circle — red when selected */
.pop-circle { fill: #1d3557; opacity: .85; transition: r .15s; cursor: default; }
.pop-circle.active { fill: #e63946; opacity: 1; }
.pop-label { font-size: 9px; fill: #1d3557; font-weight: 600; text-anchor: middle; pointer-events: none; }
/* Orange dot marking the user's click position */
.user-dot { fill: #f4a261; stroke: #e76f51; stroke-width: 1.5; }
/* Dashed line from user to nearest PoP (red) and to origin (grey) */
.conn-line { stroke: #e63946; stroke-width: 1.5; stroke-dasharray: 4 3; opacity: .7; }
.origin-line { stroke: #888; stroke-width: 1; stroke-dasharray: 3 4; opacity: .45; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// CDN Points of Presence with approximate SVG coordinates
const POPS = [
{ id: 'nyc', name: 'New York', x: 110, y: 85 },
{ id: 'lax', name: 'Los Angeles', x: 72, y: 95 },
{ id: 'sao', name: 'São Paulo', x: 118, y: 195 },
{ id: 'lon', name: 'London', x: 228, y: 47 },
{ id: 'fra', name: 'Frankfurt', x: 242, y: 52 },
{ id: 'sin', name: 'Singapore', x: 360, y: 145 },
{ id: 'tok', name: 'Tokyo', x: 400, y: 75 },
{ id: 'syd', name: 'Sydney', x: 390, y: 188 },
{ id: 'joh', name: 'Johannesburg', x: 232, y: 188 },
];
// Single origin server location (top-left of North America)
const ORIGIN = { x: 100, y: 60, name: 'Origin (VA)' };
// Simulated cache hit rate for a popular CDN
const CACHE_HIT_RATE = 0.92;
const svg = document.getElementById('map');
const popsG = document.getElementById('pops');
const userG = document.getElementById('user-group');
const linesG = document.getElementById('lines');
const statsDiv = document.getElementById('stats');
const resultDiv = document.getElementById('result');
const nearestName = document.getElementById('nearest-name');
const edgeLatEl = document.getElementById('edge-lat');
const originLatEl = document.getElementById('origin-lat');
const savingEl = document.getElementById('saving');
const cacheHitEl = document.getElementById('cache-hit');
// Draw PoP circles and labels; highlight the active PoP in red
function drawPops(activePop) {
popsG.innerHTML = '';
POPS.forEach(p => {
const isActive = activePop && p.id === activePop.id;
const r = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
r.setAttribute('cx', p.x); r.setAttribute('cy', p.y); r.setAttribute('r', isActive ? 9 : 7);
r.setAttribute('class', 'pop-circle' + (isActive ? ' active' : ''));
popsG.appendChild(r);
const t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
t.setAttribute('x', p.x); t.setAttribute('y', p.y + 18);
t.setAttribute('class', 'pop-label'); t.textContent = p.name;
popsG.appendChild(t);
});
}
// Euclidean distance in SVG coordinate space
function dist(ax, ay, bx, by) {
return Math.sqrt((ax - bx) ** 2 + (ay - by) ** 2);
}
// Convert SVG distance to a simulated millisecond latency
function latencyMs(px, py, qx, qy) {
// Scale factor maps SVG units to approximate milliseconds
return Math.round(dist(px, py, qx, qy) * 1.4);
}
drawPops(null);
// Handle map click — find nearest PoP and update the stats panel
svg.addEventListener('click', (e) => {
const rect = svg.getBoundingClientRect();
const vb = svg.viewBox.baseVal;
const scaleX = vb.width / rect.width;
const scaleY = vb.height / rect.height;
const ux = (e.clientX - rect.left) * scaleX;
const uy = (e.clientY - rect.top) * scaleY;
// Find the closest PoP by Euclidean distance
let nearest = POPS[0], nearDist = Infinity;
POPS.forEach(p => {
const d = dist(ux, uy, p.x, p.y);
if (d < nearDist) { nearDist = d; nearest = p; }
});
// Draw orange dot at the user's click position
userG.innerHTML = '';
const uc = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
uc.setAttribute('cx', ux); uc.setAttribute('cy', uy); uc.setAttribute('r', 6);
uc.setAttribute('class', 'user-dot');
userG.appendChild(uc);
// Draw red dashed line to nearest PoP and grey dashed line to origin
linesG.innerHTML = '';
const cl = document.createElementNS('http://www.w3.org/2000/svg', 'line');
cl.setAttribute('x1', ux); cl.setAttribute('y1', uy);
cl.setAttribute('x2', nearest.x); cl.setAttribute('y2', nearest.y);
cl.setAttribute('class', 'conn-line');
linesG.appendChild(cl);
const ol = document.createElementNS('http://www.w3.org/2000/svg', 'line');
ol.setAttribute('x1', ux); ol.setAttribute('y1', uy);
ol.setAttribute('x2', ORIGIN.x); ol.setAttribute('y2', ORIGIN.y);
ol.setAttribute('class', 'origin-line');
linesG.appendChild(ol);
drawPops(nearest);
// Compute and display latency stats in the side panel
const edgeLat = latencyMs(ux, uy, nearest.x, nearest.y);
const originLat = latencyMs(ux, uy, ORIGIN.x, ORIGIN.y);
const saved = originLat - edgeLat;
const cacheStr = CACHE_HIT_RATE >= 0.9 ? 'HIT (cached at edge)' : 'MISS (fetched from origin)';
nearestName.textContent = nearest.name;
edgeLatEl.textContent = i18nFmt('%n% ms', { n: edgeLat });
originLatEl.textContent = i18nFmt('%n% ms', { n: originLat });
savingEl.textContent = saved > 0 ? i18nFmt('-%n% ms', { n: saved }) : '0 ms';
cacheHitEl.textContent = i18nFmt('%pct%%', { pct: Math.round(CACHE_HIT_RATE * 100) }) + ' — ' + cacheStr;
resultDiv.classList.add('hidden');
statsDiv.classList.remove('hidden');
});
document.getElementById('btn-reset').addEventListener('click', () => {
userG.innerHTML = '';
linesG.innerHTML = '';
drawPops(null);
statsDiv.classList.add('hidden');
resultDiv.classList.remove('hidden');
resultDiv.textContent = 'Click the map to route a request.';
});
<!-- CDN anycast demo — click the map to place a user and find the nearest PoP -->
<div class="layout">
<div class="map-wrap">
<svg id="map" viewBox="0 0 500 280" xmlns="http://www.w3.org/2000/svg" aria-label="World map with CDN Points of Presence">
<!-- Ocean background -->
<rect width="500" height="280" fill="#e8f0f7" rx="8"/>
<!-- Simplified continent outlines -->
<!-- North America -->
<path d="M60,40 L140,35 L155,80 L130,120 L80,130 L55,100 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- South America -->
<path d="M105,145 L145,140 L155,190 L130,240 L95,235 L85,185 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Europe -->
<path d="M210,30 L255,28 L260,70 L235,80 L210,65 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Africa -->
<path d="M210,90 L255,85 L260,155 L235,200 L205,195 L195,145 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Asia -->
<path d="M270,25 L410,20 L415,110 L370,130 L300,125 L265,90 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- Oceania -->
<path d="M355,160 L410,155 L415,205 L375,215 L345,200 Z" fill="#c9d8e8" stroke="#a0b8cc" stroke-width="1"/>
<!-- PoP markers rendered by JS -->
<g id="pops"></g>
<!-- User click position -->
<g id="user-group"></g>
<!-- Connection lines drawn by JS -->
<g id="lines"></g>
</svg>
<p class="hint">Click anywhere on the map to place a user request</p>
</div>
<div class="panel">
<h3 class="panel-title">Routing Result</h3>
<div id="result" class="result-idle">Click the map to route a request.</div>
<div id="stats" class="stats hidden">
<div class="stat-row"><span class="stat-label">Nearest PoP</span><span id="nearest-name" class="stat-val"></span></div>
<div class="stat-row"><span class="stat-label">Edge latency</span><span id="edge-lat" class="stat-val"></span></div>
<div class="stat-row"><span class="stat-label">Origin latency</span><span id="origin-lat" class="stat-val"></span></div>
<div class="stat-row"><span class="stat-label">Latency saved</span><span id="saving" class="stat-val green"></span></div>
<div class="stat-row"><span class="stat-label">Cache hit rate</span><span id="cache-hit" class="stat-val"></span></div>
</div>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
</div>
/* Box-sizing reset and zero margins */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; color: #222; }
.layout { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
.map-wrap { flex: 1 1 300px; }
#map { width: 100%; height: auto; cursor: crosshair; border-radius: 8px;
border: 1px solid #c5d5e5; display: block; }
/* Small hint text below the map */
.hint { font-size: .8rem; color: #555; margin-top: 6px; text-align: center; }
/* Side panel card with routing results */
.panel { flex: 0 0 180px; background: #f0f5fa; border: 1px solid #c5d5e5;
border-radius: 8px; padding: 12px; display: flex; flex-direction: column; gap: 8px; }
.panel-title { font-size: .85rem; font-weight: 700; color: #1d3557; }
.result-idle { font-size: .8rem; color: #888; }
.stats { display: flex; flex-direction: column; gap: 6px; }
.hidden { display: none; }
.stat-row { display: flex; flex-direction: column; }
.stat-label { font-size: .7rem; color: #666; text-transform: uppercase; letter-spacing: .03em; }
.stat-val { font-size: .9rem; font-weight: 700; color: #1d3557; }
.stat-val.green { color: #0a7d33; }
/* Ghost button styling */
button.ghost { font: 600 13px system-ui; padding: .35rem .7rem; border: 1px solid #1d3557;
background: #fff; color: #1d3557; border-radius: 6px; cursor: pointer; margin-top: 4px; }
/* PoP marker circle — red when selected */
.pop-circle { fill: #1d3557; opacity: .85; transition: r .15s; cursor: default; }
.pop-circle.active { fill: #e63946; opacity: 1; }
.pop-label { font-size: 9px; fill: #1d3557; font-weight: 600; text-anchor: middle; pointer-events: none; }
/* Orange dot marking the user's click position */
.user-dot { fill: #f4a261; stroke: #e76f51; stroke-width: 1.5; }
/* Dashed line from user to nearest PoP (red) and to origin (grey) */
.conn-line { stroke: #e63946; stroke-width: 1.5; stroke-dasharray: 4 3; opacity: .7; }
.origin-line { stroke: #888; stroke-width: 1; stroke-dasharray: 3 4; opacity: .45; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// CDN Points of Presence with approximate SVG coordinates
const POPS = [
{ id: 'nyc', name: 'New York', x: 110, y: 85 },
{ id: 'lax', name: 'Los Angeles', x: 72, y: 95 },
{ id: 'sao', name: 'São Paulo', x: 118, y: 195 },
{ id: 'lon', name: 'London', x: 228, y: 47 },
{ id: 'fra', name: 'Frankfurt', x: 242, y: 52 },
{ id: 'sin', name: 'Singapore', x: 360, y: 145 },
{ id: 'tok', name: 'Tokyo', x: 400, y: 75 },
{ id: 'syd', name: 'Sydney', x: 390, y: 188 },
{ id: 'joh', name: 'Johannesburg', x: 232, y: 188 },
];
// Single origin server location (top-left of North America)
const ORIGIN = { x: 100, y: 60, name: 'Origin (VA)' };
// Simulated cache hit rate for a popular CDN
const CACHE_HIT_RATE = 0.92;
const svg = document.getElementById('map');
const popsG = document.getElementById('pops');
const userG = document.getElementById('user-group');
const linesG = document.getElementById('lines');
const statsDiv = document.getElementById('stats');
const resultDiv = document.getElementById('result');
const nearestName = document.getElementById('nearest-name');
const edgeLatEl = document.getElementById('edge-lat');
const originLatEl = document.getElementById('origin-lat');
const savingEl = document.getElementById('saving');
const cacheHitEl = document.getElementById('cache-hit');
// Draw PoP circles and labels; highlight the active PoP in red
function drawPops(activePop) {
popsG.innerHTML = '';
POPS.forEach(p => {
const isActive = activePop && p.id === activePop.id;
const r = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
r.setAttribute('cx', p.x); r.setAttribute('cy', p.y); r.setAttribute('r', isActive ? 9 : 7);
r.setAttribute('class', 'pop-circle' + (isActive ? ' active' : ''));
popsG.appendChild(r);
const t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
t.setAttribute('x', p.x); t.setAttribute('y', p.y + 18);
t.setAttribute('class', 'pop-label'); t.textContent = p.name;
popsG.appendChild(t);
});
}
// Euclidean distance in SVG coordinate space
function dist(ax, ay, bx, by) {
return Math.sqrt((ax - bx) ** 2 + (ay - by) ** 2);
}
// Convert SVG distance to a simulated millisecond latency
function latencyMs(px, py, qx, qy) {
// Scale factor maps SVG units to approximate milliseconds
return Math.round(dist(px, py, qx, qy) * 1.4);
}
drawPops(null);
// Handle map click — find nearest PoP and update the stats panel
svg.addEventListener('click', (e) => {
const rect = svg.getBoundingClientRect();
const vb = svg.viewBox.baseVal;
const scaleX = vb.width / rect.width;
const scaleY = vb.height / rect.height;
const ux = (e.clientX - rect.left) * scaleX;
const uy = (e.clientY - rect.top) * scaleY;
// Find the closest PoP by Euclidean distance
let nearest = POPS[0], nearDist = Infinity;
POPS.forEach(p => {
const d = dist(ux, uy, p.x, p.y);
if (d < nearDist) { nearDist = d; nearest = p; }
});
// Draw orange dot at the user's click position
userG.innerHTML = '';
const uc = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
uc.setAttribute('cx', ux); uc.setAttribute('cy', uy); uc.setAttribute('r', 6);
uc.setAttribute('class', 'user-dot');
userG.appendChild(uc);
// Draw red dashed line to nearest PoP and grey dashed line to origin
linesG.innerHTML = '';
const cl = document.createElementNS('http://www.w3.org/2000/svg', 'line');
cl.setAttribute('x1', ux); cl.setAttribute('y1', uy);
cl.setAttribute('x2', nearest.x); cl.setAttribute('y2', nearest.y);
cl.setAttribute('class', 'conn-line');
linesG.appendChild(cl);
const ol = document.createElementNS('http://www.w3.org/2000/svg', 'line');
ol.setAttribute('x1', ux); ol.setAttribute('y1', uy);
ol.setAttribute('x2', ORIGIN.x); ol.setAttribute('y2', ORIGIN.y);
ol.setAttribute('class', 'origin-line');
linesG.appendChild(ol);
drawPops(nearest);
// Compute and display latency stats in the side panel
const edgeLat = latencyMs(ux, uy, nearest.x, nearest.y);
const originLat = latencyMs(ux, uy, ORIGIN.x, ORIGIN.y);
const saved = originLat - edgeLat;
const cacheStr = CACHE_HIT_RATE >= 0.9 ? 'HIT (cached at edge)' : 'MISS (fetched from origin)';
nearestName.textContent = nearest.name;
edgeLatEl.textContent = i18nFmt('%n% ms', { n: edgeLat });
originLatEl.textContent = i18nFmt('%n% ms', { n: originLat });
savingEl.textContent = saved > 0 ? i18nFmt('-%n% ms', { n: saved }) : '0 ms';
cacheHitEl.textContent = i18nFmt('%pct%%', { pct: Math.round(CACHE_HIT_RATE * 100) }) + ' — ' + cacheStr;
resultDiv.classList.add('hidden');
statsDiv.classList.remove('hidden');
});
document.getElementById('btn-reset').addEventListener('click', () => {
userG.innerHTML = '';
linesG.innerHTML = '';
drawPops(null);
statsDiv.classList.add('hidden');
resultDiv.classList.remove('hidden');
resultDiv.textContent = 'Click the map to route a request.';
});
Comments
Loading comments...