惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
Google DeepMind News
Google DeepMind News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
小众软件
小众软件
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
A
About on SuperTechFans
Vercel News
Vercel News
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
S
SegmentFault 最新的问题
V
Visual Studio Blog
T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Azure Blog
Microsoft Azure Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
美团技术团队

Starred Articles

Why some people mow a lawn better than others Improvements to Web for AI Should Benefit All Users chriscoyier.net When Sites Need to Walk Away AI & Alignment New to the web platform in April  |  Blog  |  web.dev My house alerts me when my cat climbs into the ceiling Eames Pavilion System Squarespace & Web Standards: How We Helped Bring HTML Video & Audio Lazy Loading to Today’s Browsers | Scott Jehl, Web Designer/Developer The Web is a Guitar Amp Now (Literally) Claude is an Electron App Making keyboard navigation effortless Miscalibrated I used Claude Code and GSD to build the accessibility tool I’ve always wanted - blakewatson.com Sizing chaos The Good & Not Good Scroll indicators on tables with background colours using animation-timeline Webspace Invaders Fresh Hot CSS: Trig Functions The Year Of The Linux Desktop (for fitness games) Almost Plain Text, Nicely Done – Email is good. Uncrate's 100-ish favorite things on Amazon VS Code – highlight just the active indent guide How musicals use motifs to tell stories Good Tidings! Review: MoErgo Go60, a split ergonomic and fully programmable keyboard The line and the stream. — Ethan Marcotte Testing HTML Light DOM Web Components: Easier Than Expected! Enhancing Web Components Safely with Self-Destructing CSS | Scott Jehl, Web Designer/Developer Steam Machine
Delivering a dynamic hexagonal world map in just 10kb | C...
Ben Schwarz · 2026-04-22 · via Starred Articles

Ok, let me get this out in the open: I love the new hex map in Calibre, maybe more than I should. Two weeks of wrestling with point-in-polygon calculations, tolerance settings, and the question of whether Tasmania is big enough to fill a 4-pixel hexagon (dear reader, it is not), and I’d do it all again. Let me show you how I built hex map.

Mapping real user sessions by performance#

"Hex map" is a dynamic map visualisation summarising user experience sessions from each country, displayed in the Real User Monitoring (RUM) Audience report.

Calibre’s RUM Audience report. You can see the hex map at the bottom of the image.
Calibre’s RUM Audience report. You can see the hex map at the bottom of the image.

The map is made up of interlocked hexagons, each representing a country. The colour of each country indicates Calibre’s UX Rating, based on overall performance.

As user sessions are observed, countries light up in the map. You can gain a lot of empathy and understanding when segmenting by UX Rating.

The map, cycling through grading colours for fun. It isn’t normally this hectic.

The map, cycling through grading colours for fun. It isn’t normally this hectic.

Design goals#

After viewing maps in products I knew, and surveying existing libraries and paid mapping services, it was clear I wanted something more simple, elegant, and maybe a bit more artistic. The requirements were:

  • Load fast, and feel light-weight
  • Visually display country-level UX Rating
  • Intentionally low-fi, a visual element to enjoy and tell a story
  • Work on all browsers
  • Have light and dark colour mode themes to match the app

Research and inspiration#

With any creative data visualisation project, you want to look for examples in the wild. A few things kept pulling me in: the low-fi blockiness of voxels, numerous beautiful election voter maps, and more abstract geo visualisations.

Voxel geometry, hexagonal maps and abstract geography.

I’ve always enjoyed the datavis used on election nights. Providing valuable illustrations for viewers who likely aren’t used to looking at visualisations is a bit of an art form. Hex cartograms do a great job of compacting data into beautiful visual elements that help tell a story without being too much.

Given a brief of visual simplicity, hexagonal maps were perfect and given my experience with SVG and D3, I knew I could quickly iterate to a high quality map.

Getting the detail right#

I wanted the map to feel blocky and not too detailed. Hexes at about 4 pixels wide felt right, low-fi enough to feel like an illustration rather than a satellite image.

Where the real work came in, was the map data itself. High-resolution geojson is hefty, but can be significantly reduced by simplifying paths (country borders). Oversimplify and countries merge, islands disappear. It’s a balance.

I started with 10 meter accuracy country boundaries and ran them through Turf’s excellent simplify function, iterating on the tolerance until countries were still recognisable at 4 pixels without overwhelming the visual with detail.

An early render of the map with cities highlighted

An early render of the map with cities highlighted

One shape, repeated 2,164 times#

Once the geometry was behaving, it was time to start narrowing into a final working implementation.

I wrote a pipeline in Node.js, it needed to:

  • Source input map data
  • Simplify / compress geojson data
  • Include country names and 2 letter country codes
  • Draw the map using equirectangular projection† (d3-geo)
  • Centre the map with a slight north westward offset
  • Remove Antarctica
  • Render a hexagon grid, locating country boundaries using point-in-polygon and boundary detection for each hex
  • Remove hexes for water
  • Add ARIA roles and labels
  • Render to SVG

Map "projections" are the method used to draw a map. There’s many interesting interpretations of map projections, but most people are familar with equirectangular.

Rendering a static .svg file meant no client-side mapping data, no network calls after the initial load. A purely visual representation in one file.

Because the map is made of one shape repeated thousands of times, I leaned on SVG’s <symbol> and <use> to avoid redrawing it and creating a bloated SVG file. Define the hex once:

<symbol id="hex">...</symbol>

Then reference hex wherever it needs to appear, grouped by country:

<g class="country-group" data-country="AU">
	<use href="#hex" x="..." y="..." />
	<use href="#hex" x="..." y="..." />
</g>

That’s the entire data model, structure and identifiers. The output is a single 144kb SVG. The map is generated each time we build and deploy, gets cached in the CDN, and is compressed down to just 10kb when served with GZip.

Comparing 10kb to full blown maps would be an unfair comparison, as they usually have drag to pan, zoom, tiling, internationalisation, and a lot more features. I won’t make that comparison directly.

Light, dark and glow#

Now that I had a .svg file, I added CSS to specify grading colours, and apply light and dark theming with light-dark:

svg {
	--hex-color: light-dark(var(--brand-grey-100), var(--brand-grey-500));
	--hex-stroke: light-dark(var(--brand-grey-200), var(--brand-grey-700));
	--hex-stroke-width: 1;
	--hex-transition: all 0.2s ease;

	/* also define --hex-good-color, --hex-good-stroke, --hex-needs-improvement-color,--hex-needs-improvement-stroke, --hex-poor-color, --hex-poor-stroke here. */

	& use {
		fill: var(--hex-color);
		stroke: var(--hex-stroke);
		stroke-width: var(--hex-stroke-width);
		transition: var(--hex-transition);
	}
}

Our in-app report page dynamically loads the SVG, and assigns grading colours to each country <g> with CSS variables, e.g.: hex-color: var(--hex-needs-improvement-color). Finalising with radial background to give the countries some glow against the background.

Sorry, Tasmania#

A few things didn’t make it. The United States wraps around the left and right edges of the map. Tasmania isn't large enough to register a 4 pixel dot (sorry mates!). There are many imperfections gained with this particular approach to simplification.

Getting a map that’s perfectly accurate, up-to-date with every disputed territory, aligned with our geolocation dataset, fast, and nice to look at would require a cartographer, a geopolitical advisor, and a lot more than two weeks. I am none of those things.

What the map does do: show users where their audience is and how they’re doing. And I love it.

If you want to see the map in action, sign up for a free trial and add real user monitoring to your website.