The open-source platform for interactive storymaps, participatory maps, data visualization and geospatial presentations.
TalkingMaps lets you create scroll-driven story maps that combine interactive 2D and 3D maps, text narratives, multimedia, charts and data layers. It's designed for journalists, researchers, educators, planners and anyone who needs to tell a story on a map.
Key features:
http://localhost:8080)After logging in you'll see the Dashboard with:
Each story card has a three-dot menu (…) with actions: Edit, Preview, Publish/Unpublish, Duplicate, Export, Share, Delete.
In the Editor, click the gear icon () to access story-level settings:
The editor has three panels:
| Left — Slides | Center — Map | Right — Properties |
|---|---|---|
| List of slides. Drag & drop to reorder. Click + to add a new slide. | Interactive map preview. Use the toolbar to add markers, manage layers, toggle 3D. | Properties for the selected slide: layout, text, map view, styling, data, media. |
The right panel has several tabs:
Each slide can have a different layout. Choose the layout in the Content tab of the slide properties.
| Layout | Description | Map visible? |
|---|---|---|
| Cover | Centered title and text, ideal for the opening slide. Optional background image/video. | No |
| Side Left | Text panel on the left, map on the right. The default for map-driven narratives. | Yes |
| Side Right | Text panel on the right, map on the left. | Yes |
| Center | Text overlaid at the center of the map. | Yes |
| Full Map | Map fills the entire screen. No text panel. | Yes |
| Navigable Image New | Display a high-resolution image (painting, floor plan, historical map) that users can zoom and pan. Configure the image URL in the Map tab. | Yes (image) |
| 3D Globe | CesiumJS 3D globe with terrain, buildings, and 3D Tiles. Configure tileset in Media & 3D tab. | Yes (3D) |
| Point Cloud | Potree-based LiDAR / point cloud visualization. Configure the point cloud URL in Media & 3D tab. | Yes (3D) |
| Full Media | Full-screen image or video with optional text overlay at the bottom. | No |
| Text Only | Text-only slide for narratives, quotes, deep-dives. No map. | No |
| Text + Media | Split view: text on one side, image/video on the other. | No |
| Separator | Large title to divide sections of your story. | No |
The narrative text editor supports:
The editor keeps a history of up to 50 snapshots of the whole slide deck, so a mistake is never more than one keystroke away from being undone.
A snapshot is taken before each structural change: adding, deleting or reordering a slide, changing a layout, and when you start typing in the title or narrative. The buttons are greyed out when there is nothing left to undo.
At the top of the narrative area you'll find quick action buttons to insert images, videos, charts or iframes with a single click.
A slide can carry a gallery: a responsive carousel with captions, thumbnails, keyboard arrows and touch swipe. Open Image gallery in the Media tab, then Add gallery.
Pick images by uploading them or choosing from the media library, and give each one a caption. Two switches control playback: autoplay advances every few seconds, and thumbnails shows the strip beneath the main image. Readers can also open an image full screen.
Sometimes a paragraph needs a small map of its own rather than a move of the main one — a location mentioned in passing, a detail inset. The button in the narrative toolbar inserts an express map: a self-contained mini-map placed inline in the text.
The dialog opens on the current map view, and clicking the preview sets the coordinates. Choose the zoom, whether to drop a marker, and whether the reader may pan it. The result sits in the narrative flow like an image.
Any word in the narrative can become a link that drives the map instead of navigating away. Select the text, press in the toolbar, and choose what it should point at:
| Target | What happens on click |
|---|---|
| Marker | The map flies to that marker at close zoom. |
| Layer | The layer is switched on and pulses briefly so the reader's eye finds it. |
This is what turns a paragraph into an interface: "the eastern district" becomes something the reader can press to be taken there, without leaving the sentence.
Three scroll-driven effects that make a data story feel alive. All three are per-slide and configured in the properties panel.
A grid of headline numbers that count up from zero when the slide scrolls into view — the standard way to open a data story with its most striking statistics.
In the Media tab, open Key figures and click Add figure. Each figure has:
| Field | Description |
|---|---|
| Value | The target number. It is formatted with thousands separators in the reader's language. |
| Prefix / Suffix | Units around the number, e.g. $ and B, or km². |
| Decimals | How many decimal places to show (0–6). |
| Label | The caption under the number. |
| Description | An optional smaller line of context. |
Columns sets the grid width (1–4; it collapses automatically on mobile) and Duration the count-up length in milliseconds — set it to 0 for no animation. Readers who ask their system for reduced motion always see the final values immediately.
Beyond the classic Fade / Slide / Zoom, the Transition selector in the Slide tab offers two entrance animations:
| Transition | Description |
|---|---|
| Word reveal | The slide title animates in one word at a time, each sliding up from behind a mask. Best on cover and chapter-opening slides. |
| Staggered blocks | Paragraphs, lists and embedded blocks fade in one after another instead of all at once. Best on text-heavy slides. |
Word reveal only touches plain-text headings — a title containing links or other markup is left as it is.
Two images stacked with a draggable divider, for showing change over time: satellite imagery from two dates, a site before and after restoration, a historical photo against a modern one.
In the Media tab, open Image comparison, pick the "before" and "after" images (upload or paste a URL) and optionally label them (e.g. 2010 and 2024). Both images are required — clearing either one removes the widget.
Aspect ratio controls the shape of the frame and Initial handle position where the divider sits when the slide opens. Readers can drag the handle, click anywhere on the image to jump the divider there, or focus it and use the arrow keys (Shift for larger steps, Home/End to snap to either edge).
For each slide you can set a specific map position (center, zoom, bearing, pitch):
When the viewer transitions to a slide, the map animates to its saved position. Choose the animation:
| Animation | Description |
|---|---|
| Fly To | Smooth aerial arc (default). Best for long-distance transitions. |
| Ease To | Linear smooth pan. Best for short-distance transitions. |
| Jump To | Instant, no animation. Best for same-area layout changes. |
| Cinematic New | Three-step transition: zoom out, pan across, zoom in. Best for dramatic long-distance jumps between locations. |
Each slide can use a different basemap. Select it in the Map tab dropdown. This is useful for comparing satellite vs. street views across slides.
Enable a swipe comparison between two basemaps on a single slide. In the Style tab, toggle "Compare" and select a second basemap.
Animate a layer through time: features appear cumulatively as the cursor advances, so the reader watches the pattern build up. In the Map tab, toggle Timeline and fill in:
| Field | Description |
|---|---|
| Layer | Which of the slide's layers to filter. |
| Date field | The feature property holding the date or year, e.g. year, date, timestamp. |
| Start / End | The range to play through. Either plain numbers (2000 → 2023, one step per year) or dates (2000-01-01 → 2000-12-31, 60 steps). |
| Speed | Slow, Medium or Fast — how long each step is held. |
The viewer shows a playback bar at the bottom of the slide: play/pause, a scrubber and the current value. It starts playing when the slide comes into view and stops at the end; the button then offers a replay.
2020-05-01 or 2020-05-01T09:30:00Z, which sort correctly as text. Features missing the field are hidden for the whole animation. On a clustered layer the clusters are hidden while the timeline runs, since a cluster is aggregated by the source and cannot be filtered feature by feature.
Attach an audio track to a slide — a voiceover, an interview clip, a field recording. Upload it under Audio in the Slide tab; a compact transport control (play/pause, scrubber, elapsed time) appears inside the slide card.
Switch on Autoplay to start it as the slide arrives. Note that browsers block autoplay of audible media unless the reader has already interacted with the page, so treat it as a hint rather than a guarantee — the play button is always there. The track stops automatically when the reader scrolls to another slide.
TalkingMaps supports multiple data source types that can be added as map layers:
| Type | Description |
|---|---|
| GeoJSON | Upload a .geojson file from your computer. Points, lines and polygons are supported. |
| Shapefile | Upload a .zip containing Shapefile files. Auto-converted to GeoJSON server-side with reprojection to WGS84. |
| GeoPackage | Upload a .gpkg file. Auto-converted to GeoJSON server-side. |
| WMS | Web Map Service. Enter the URL and layer name. Use Discover layers to browse available layers via GetCapabilities. |
| WFS New | Web Feature Service. Enter the URL and feature type. Features are fetched as GeoJSON via proxy. Use Discover layers to browse. |
| WMTS / XYZ | Tiled services. Enter the tile URL template with {z}/{x}/{y} placeholders. |
| Vector Tiles New | Mapbox Vector Tiles (MVT/PBF) from a tile server. Enter the tile URL template and source layer name. |
| Cloud Optimized GeoTIFF | COG rasters served directly from a URL, streamed on-the-fly. |
In the Layers section of each slide's properties:
This lets you progressively reveal data as the story unfolds, or fade layers in and out for emphasis.
Access the global layer catalog from the user menu → Layers. Layers are shared across all your stories.
Save your personal GIS service endpoints (GeoServer, QGIS Server, MapServer, or any OGC-compliant service) and reuse them across all your stories.
| Type | Description | Capabilities |
|---|---|---|
| WMS | Web Map Service — raster map images | GetCapabilities parsed to list layers |
| WFS | Web Feature Service — vector features as GeoJSON | GetCapabilities parsed to list feature types |
| WMTS | Web Map Tile Service — pre-rendered tiles | Manual configuration |
| XYZ | XYZ tile servers (OpenStreetMap-style) | Manual configuration |
| Vector Tiles | Mapbox Vector Tiles (MVT/PBF) | Manual configuration |
The editor can pull context straight from two public sources, so a story about a place does not have to be researched somewhere else and pasted in. No API key is needed for either.
Two ways in: nearby finds articles with coordinates around the current map centre, and search looks one up by title. Results carry a summary and a thumbnail, and can be inserted into the narrative or turned into markers.
The language of the Wikipedia edition to query is selectable — an Italian story usually wants it, but the English edition is often richer for places outside Italy.
Points of interest nearby queries the Overpass API around a point, filtered by category (food and drink, culture, transport, services, and so on) within a radius you choose. Each result can become a marker with its name and type already filled in.
Typing a place name in the editor's search box moves the map there. The reverse also works: clicking the map while adding a marker fills in the address, so a marker gets a sensible title without typing one.
CKAN is the software behind most public open-data portals — dati.gov.it, data.europa.eu and many regional ones. TalkingMaps can search those catalogues and import a geospatial resource directly as a layer.
Open Manage layers in the editor and press Open Data (CKAN). Pick a portal, type what you are looking for, and press Search.
Four Italian portals ship preconfigured — the national dati.gov.it catalogue and the Tuscany, Emilia-Romagna and Trentino ones. Any other CKAN instance works too: choose Another portal… and paste its address.
/api/3/action — which is not always the domain root. The national portal, for instance, serves its catalogue at https://dati.gov.it/opendata. If you paste something that is not a CKAN endpoint, the error message says so rather than failing silently.
Results list each dataset with its publishing organisation and the resources it offers. Only the formats that can become a map layer get an Import button; the rest are listed so you can see what else is in the dataset.
| Format | What happens |
|---|---|
| GeoJSON | Imported directly as a vector layer, styled by geometry type. |
| CSV | TalkingMaps reads the header and asks which columns hold latitude and longitude, guessing the usual names. Rows with unreadable coordinates are skipped. |
| WMS / WFS | Registered as a remote service layer rather than being copied. |
| Everything else | Listed but not importable — download it and use Layers & Data Sources instead. |
An imported resource becomes a normal layer: it is added to the current story straight away, appears in your layer catalogue for reuse, and can be restyled like any other.
| Endpoint | Purpose |
|---|---|
GET /api/ckan/portals | The list of known portals, with their CKAN root URLs. |
GET /api/ckan/search | Search datasets on a portal. Takes portal_url, q and an optional format_filter. |
GET /api/ckan/resource | Fetch one resource — GeoJSON as-is, CSV parsed into columns and rows. |
POST /api/ckan/import-as-layer | Import a resource as a layer. Body: url, name, format, plus lat_field and lon_field for CSV. |
Turn point data into a heatmap visualization to show density patterns.
The heatmap uses MapLibre's native GPU-accelerated rendering, so it performs well even with thousands of points. The color ramp goes from transparent (low density) through blue, cyan, green, yellow to red (high density).
Markers are per-slide: each slide shows only its own markers. In the viewer, markers appear with a popup on click.
In the Data tab, use the Chart Wizard or paste a JSON configuration:
{
"type": "bar",
"labels": ["Jan", "Feb", "Mar", "Apr", "May"],
"data": [120, 190, 300, 250, 420],
"options": { "label": "Monthly visitors" }
}
Supported chart types: bar, line, pie, doughnut, scatter, radar, polarArea. Horizontal bars: set "horizontal": true in options.
Add dashboard-style KPI cards to any slide. In the style overrides:
{
"dashboard": [
{"label": "Population", "value": "59.5M", "icon": "people", "color": "#1a73e8"},
{"label": "Area", "value": "301,340 km²", "icon": "geo-alt", "color": "#34a853"},
{"label": "Municipalities", "value": "7,904", "icon": "building", "color": "#fbbc04"}
]
}
Embed interactive timelines powered by TimelineJS. In the Data tab, paste the timeline JSON data. Timelines are great for historical storymaps.
Customize how your layers look on the map:
Access the symbology editor from the layer panel. You can also use presets (Red-Yellow-Green, Blue-White-Red, Viridis, etc.) for quick setup.
Create slides with a 3D globe layout to show your data on a realistic 3D terrain with CesiumJS.
You can load 3D Tilesets from:
tileset.json fileVisualize LiDAR and point cloud data using Potree.
Display a high-resolution image (painting, floor plan, historical map, aerial photo) as a zoomable, pannable layer — just like a map.
Users can zoom in/out and pan the image in the viewer, just like they would with a map. This is perfect for art analysis, architectural plans, or detailed diagrams.
Enable participatory mode to let registered users add geolocated contributions (points with text, photos, videos, or audio) to your story map.
| Who | Action |
|---|---|
| Registered users | Click the Contribute button in the viewer, then click on the map to place a point. Fill in title, description, category, and optionally attach a photo, video or audio file. |
| Story owner / Admin | Review contributions in the Moderation panel (Dashboard → story menu → Moderate). Approve or reject each contribution with an optional note. |
| Everyone | See approved contributions as markers on the map with popups showing the content and media. |
The Contribute button only appears on slides that show a map: on a cover or text-only slide the map is behind the text panel, so there is nothing for the reader to click. Give a participatory story at least one map slide.
Contribution markers are coloured by category, following the order of the category list you define, so readers can tell one kind of report from another at a glance.
The maximum file size for contribution attachments is configurable by the administrator (default: 10 MB). See System Settings.
TalkingMaps integrates AI capabilities to help you create content faster:
Configure your API keys in the user menu → Account → AI Settings. Keys are encrypted and stored per-user.
Invite other users to collaborate on your stories:
Collaborators see the story in their dashboard and can access it based on their assigned role.
A version is a named snapshot of every slide in a story, taken on demand. It is the checkpoint that undo is not: it survives a reload, a logout, and a week of further editing.
Embed a story in any website using an iframe:
<iframe src="https://your-server.com?story=STORY_ID"
width="100%" height="600" frameborder="0"
allow="fullscreen"></iframe>
The embedded viewer hides the toolbar and close button for a clean reading experience.
A story is read one of two ways, chosen per story under Navigation mode in the Slide tab.
| Mode | How the reader moves |
|---|---|
| Guided (default) | Slide after slide, in the order you wrote them. The narrative leads; the map follows. |
| Free | A row of numbered buttons appears in the viewer and any slide can be reached directly, in any order. |
Guided suits an argument that builds. Free suits a story the reader is meant to browse — a catalogue of places, a set of cases where no one order is the right one.
When viewing a story, the following controls are available:
| Control | Action |
|---|---|
| Scroll wheel | Navigate between slides |
| Arrow Up / Down | Previous / next slide |
| Arrow Left / Right | Previous / next slide |
| Space | Next slide |
| Escape | Close the viewer |
| F | Toggle fullscreen |
| Map icon | Switch basemap |
| 3D icon | Toggle 2D / 3D view |
| Search icon | Geocode a place name and fly to it |
| Share icon | Copy link to share |
Access the media library from the user menu → Media Library.
Upload and manage 3D model files (glTF, GLB) for use in your stories:
.glb or .gltf fileAdmin only. User menu → Users.
| Role | Permissions |
|---|---|
| Admin | Full access: manage users, basemaps, system settings, all stories |
| Editor | Create/edit own stories, upload layers & media, collaborate on shared stories |
| Viewer | View public and shared stories only |
Actions: Create user, Disable/enable (toggle), Reset password.
Users can self-register from the login page. New users are created with the editor role by default.
Admin only. User menu → Basemaps.
Basemaps are the background maps available in all stories. Default basemaps:
To add a custom basemap: enter name, type (xyz, wms, wmts), URL, and optional config JSON (attribution, maxzoom, layers for WMS).
Admin only. User menu → Settings.
| Setting | Description | Default |
|---|---|---|
cesium_ion_token | Cesium Ion access token for 3D tilesets | (empty) |
default_storage_limit_mb | Default storage quota per user | 1024 MB |
max_upload_size_mb | Max single file upload size | 50 MB |
participatory_upload_limit_mb | Max upload size for participatory contributions | 10 MB |
analytics_enabled | Enable story view tracking | true |
# Clone the repo
git clone https://github.com/fgianoli/talkingmaps.git
cd talkingmaps
# Copy and edit the environment file
cp .env.example .env
# Edit .env: set SECRET_KEY, POSTGRES_PASSWORD, ADMIN_PASSWORD, ALLOWED_ORIGINS
# Start everything
docker compose up -d --build
# Open http://localhost:8080
# Pull latest code
git pull
# Rebuild and restart
docker compose up -d --build
For production, add a reverse proxy with HTTPS in front (Nginx Proxy Manager, Traefik, or Caddy):
# Example with Caddy (automatic HTTPS):
caddy reverse-proxy --from maps.yoursite.com --to localhost:8080
# Backup database
docker compose exec db pg_dump -U talkingmaps -d talkingmaps > backup_$(date +%Y-%m-%d).sql
# Backup media uploads
docker cp $(docker compose ps -q backend):/var/www/uploads ./uploads_backup
# Restore database
docker compose exec -T db psql -U talkingmaps -d talkingmaps < backup.sql
The WMS/WFS proxy handles CORS for external services. There are two ways to allow hosts:
backend/routers/wms_proxy.py and add hosts to ALLOWED_HOSTS, then docker compose restart backendThe full REST API documentation is available at /api/docs (auto-generated by FastAPI).
TalkingMaps is open source software. Made with passion by Federico Gianoli and Martino Boni (Tenoli).
GitHub
·
Report bug
·
Request feature