// JAVASCRIPT MAPPING FRAMEWORK

Turn data into maps.
In the browser.
With one script.

Every iXMaps layer reads top-to-bottom — data → binding → type → style — a declarative grammar for thematic maps, equally legible to you and to your AI agent. No build step, no bundler: one script tag from a CDN.

ixmaps://live · world GDP per capita 2023 · interactive legend

This map was created by Claude Code guided by iXMaps skills. See the code ↗ · see only the iXMaps code: ixmap_gdp_minimal ↗

// WHY IXMAPS

A mapping toolkit that stays out of your way.

Built for people who want map code they can still read six months later. Everything below ships in the box.

.layer().data().type()

Fluent chainable API

Pick a layer, bind your data, choose a type, define — reads top-to-bottom.

CSV · GEOJSON · PARQUET · +10 MORE

Data from anywhere

Point a layer at a URL — CSV, GeoJSON, TopoJSON, Parquet & ten more. iXMaps fetches, joins and renders.

PapaParse topojson-client DuckDB WASM flatgeobuf geobuf pbf geoparquet json-stat.js
CHOROPLETH · SYMBOLS · PIE · BAR

Thematic types built-in

Choropleths, proportional symbols, pie & bar markers — switch with one keyword.

Pie chart
PIE
Bar chart
BAR HORZ LEFT UP
Symbol chart
SYMBOL SEQUENCE CENTER

All visualization types ↗

// A GRAMMAR YOU CAN READ

Include one script. Chain your layers. Ship a map.

gdp-world.js
<script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-flat@1/ixmaps.js"></script>  <script>    const GEO_URL = "https://gisco-services.ec.europa.eu/distribution/v2/countries/topojson/CNTR_RG_60M_2020_4326.json";    const DATA_URL = "https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-data@main/world-gdp-2023/world_gdp_2023_data.csv";    const myMap = ixmaps.Map('map', {      mapType: 'white',      mapProjection: 'equalearth',      mode: 'pan', legend: 'open', align: 'center', tools: false    })      .view([0, 0], 1);    // chain 1 · geometry layer (FEATURE) — GISCO/Eurostat    myMap.layer('countries')      .data({ url: GEO_URL, type: 'topojson' })      .binding({ geo: 'geometry', id: 'CNTR_ID', title: 'NAME_ENGL' })      .type('FEATURE')      .style({ colorscheme: ['none'], linecolor: '#7a9ab0', linewidth: 0.4 })      .define();    // chain 2 · data join + classification (CHOROPLETH|QUANTILE) — World Bank 2023    myMap.layer('countries')      .data({ url: DATA_URL, type: 'csv' })      .binding({ lookup: 'id', value: 'gdp', title: 'name' })      .type('CHOROPLETH|QUANTILE')      .style({        colorscheme: ['#deeaf5', '#b3cde3', '#74b3d8', '#3182bd', '#08519c'],        fillopacity: 0.82      })      .meta({        name: 'countries_gdp',        tooltip: "<b>{{name}}</b><br>GDP <b>${{gdp}}k</b>"      })      .title('GDP per Capita (USD k)')      .define();  </script>
// THE GRAMMAR STAYS LIVE

Change it on the living map.

A theme keeps its name after rendering. Address it at runtime — filter it, restyle it, or swap the whole visualization with remove-then-define — no reload, no rebuild. You already did this: the classification switcher on the map at the top is exactly these calls.

later, at runtime
myMap.then(api => {  // 'countries_gdp' — the theme declared above, found by its name  api.changeThemeStyle('countries_gdp', 'filter:WHERE gdp > 20', 'set');  api.changeThemeStyle('countries_gdp', 'fillopacity:0.5', 'set');  api.changeThemeStyle('countries_gdp', 'filter', 'remove');  // back to all countries});

Full runtime API in the docs ↗ · switching the type live: swappable themes example ↗

// SHIP IT

Start mapping your data.

Drop this in your page
<script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-flat@1/ixmaps.js"></script>

Served from the CDN — no npm package needed, nothing to install.