Skip to content
Plate 01The spatial studio

The spatial studio for stories,
briefings and live dashboards.

People don't share shapefiles. They share stories, slides and dashboards. Spatly is the layer between raw GIS data and human attention: one project, one map, four publishable experiences, fed by live data and driven by your agents over MCP.

No credit card3 projects freeMCP on every planMapLibre, no vendor lock
41°05′N29°08′E
loading basemap…
Drag to pan. The camera drifts around a saved beat.

Basemap © OpenFreeMap · OpenMapTiles · data © OpenStreetMap contributors

03Templates · running here

Three templates, embedded on this page with the same script tag you would use.

Nothing below is a picture. Each frame is a published Spatly surface loaded through /embed.js: a live insight card polling a REST source, a linked dashboard over 34,936 power plants, and a scroll story. Copy any of them into your workspace and take it apart.

All templates
ISS right nowliveOpenDetails
Scroll to load the live embed
/embed/G9P5cdBirtlazy
World power plantsOpenDetails
Scroll to load the live embed
/embed/CQhD7iR83jlazy
A century of great earthquakesOpenDetails
Scroll to load the live embed
/embed/dVdGXT779Ylazy

Each frame reports spatly:ready when its map is up, and the badge in the frame's footer turns live when the message arrives. Embeds resize themselves through spatly:resize.

04The Studio

One editor for all four surfaces. The preview is the real viewer.

Scenes on the left, the published viewer in the middle, the inspector on the right. Move the map and press Save beat; drag a layer, a widget or a mark from the Library; switch story → slides → dashboard → insight without leaving the project. These are screenshots of the product, not mock-ups.

Open the Studio
04Studio · story mode
Spatly Studio in story mode: scenes rail, live viewer canvas with the map, beat inspector
Studio · story mode · beat inspector41.0082°N 28.9784°E · z 1.6 · ⟂ 0°
Spatly Studio in dashboard mode: 24-column grid with map, KPIs, bar chart, filters and table
Dashboard grid15.0000°N 10.0000°E · z 1.6 · ⟂ 0°

Dashboard grid. 24 columns, real drag and drop, widgets bound to metrics that recompute for the viewport.

Spatly Studio in slides mode: 16:9 canvas with frames over the map, slide list on the rail
Slides canvas39.0000°N 98.0000°W · z 3.4 · ⟂ 0°

Slides canvas. 16:9 frames over the map, one beat per slide, presenter notes on the rail.

Layer style editor: classification, ramp, opacity and legend for a choropleth layer
Layer style editor25.0000°N 15.0000°E · z 1.7 · ⟂ 0°

Layer style editor. Classification, ramps, size and height targets, and labels, all compiled to MapLibre paint.

Actions inspector: when a widget emits an event, which widgets filter, fly or flash
Actions graph15.0000°N 10.0000°E · z 1.6 · ⟂ 0°

Actions graph. “When this widget emits that, filter, fly or flash these”. One inspector, no modal tabs.

Command palette (⌘K) over the Studio: search commands, blocks, layers and beats
⌘K palette10.0000°N 20.0000°E · z 1.5 · ⟂ 0°

⌘K palette. Search commands, blocks, layers and beats from the keyboard.

Spatly Studio in insight mode: collapsed card and expanded layout bound to the same beat
Insight container15.0000°N 10.0000°E · z 1.6 · ⟂ 0°

Insight container. Collapsed card and expanded drawer designed as two layouts on the same beat.

03One concept · the Beat

Save the map state once. Every surface replays it.

A Beat is pure target state: where the camera is, which layers are visible at what opacity, which annotations are on, what time it is. Stories, slides, dashboards and insights don't own their own map logic. They reference beats and decide only how to get there: fly, ease, jump, or tween against scroll.

  • Move the map, press Save beat. Duplicate and nudge. Interpolate between two beats at any progress from 0 to 1.
  • Convert a story to slides (chapter → slide) and back, because they were never different objects.
  • Every beat has a deep link (#ch=3, ?beat=). Agents create beats through the same API you use.
  • Camera
  • Layer states
  • Annotations
  • Time
Beat
  • Story chapter
  • Slide
  • Dashboard bookmark
  • Insight expanded state
beat.camera · beat.layerStates · beat.annotationIds · beat.time
04Live by default

Dashboards and insights are never a screenshot.

Every live binding carries a refresh interval, a cache, a last-success time and an error surface. Widgets show an as-of stamp; when a source falls behind, the card goes hatched, visibly stale rather than silently wrong. Stories and slides can freeze a snapshot or stay live, per layer.

demo values · refresh pending · as of --:--:--
Active incidentsLive
122
as of --:--:--30s
Vehicles in AOILive
1483
as of --:--:--15s
M4+ last 24 hLive
31
as of --:--:--60s
Mean responseStale
6.7min0.4
last success 14 min agostale
Poll with ETag. URL GeoJSON, REST APIs, webhooks, scheduled pulls. Server-side fetch cache so a thousand viewers don't hit your API.
Metric alerts. “M5+ inside this polygon > 0” sends an e-mail with a cooldown. Dashboards stop being passive.
Live source catalog. USGS earthquakes, NASA EONET, NWS alerts, OpenSky flights, GDACS. No API key, one click, styled and with metrics ready.
05Insight Containers

The whole dashboard, folded into one card you can put anywhere.

Collapsed: a designed card with a mini map, two to four KPIs and one sentence. Expanded: the full workspace with filters and an explorable map. Same data, same beat, two layouts. Drop it on a dashboard, into a story chapter, on your marketing site or inside your own app via SDK.

This one is real: filter by category, click a site, open and close it. Nothing here is a screenshot.

Dashboard widgetStory chapterWebsite embedSDK
Corridor status · Istanbul
live
41.02°N 28.95°E · z 9.4
On time
3
Delayed
2
Closed
1

50% of monitored nodes on time. 2 delayed, 1 closed. Rail delays concentrate on the Asian side.

as of 12:04:31 · 30 s
06Built for agents

Your agent gets the same studio you do.

Spatly ships an official MCP server. Point Claude, Cursor, Copilot or ChatGPT at your workspace and they can describe layers, query features inside a bbox, save beats, write chapters and publish, all through the same REST API and the same permission model. Read tools on every plan; write tools scoped by token.

claude_desktop_config.json · Cursor · Claude Code
{
  "mcpServers": {
    "spatly": {
      "command": "npx",
      "args": ["-y", "@spatly/mcp"],
      "env": { "SPATLY_TOKEN": "spk_…" }
    }
  }
}
or remote
# Remote (streamable HTTP), no install
URL     https://spatly.io/mcp
Header  Authorization: Bearer spk_…
Tools
list_projectsget_projectcreate_projectlist_layersdescribe_layerquery_featuresspatial_filtersearch_featuresgeocodereverse_geocodelist_beatsset_map_viewcreate_story_chapterlist_kpisget_kpi_valuequery_timeseriescreate_insight_containerpublish_share_link

Agent-safe by construction: every list paginates, query_features is capped by the workspace budget (500 rows per call, 60 calls per minute by default) and can require a bbox. An agent cannot dump a dataset by accident.

Example sessionspatly · 18 tools · budget 500/60
  1. You

    Create a 6-chapter story from this GeoJSON. It's the Kahramanmaraş aftershock sequence. Lead with the two mainshocks, end on the 20 Feb Hatay event.

  2. create_project{ name: "Kahramanmaraş 2023", basemapId: "noir" } → prj_8f2
  3. describe_layer4,812 features · point · fields: mag, depth_km, time (datetime) · bbox 35.9,36.2 → 39.1,38.6
  4. query_featureswhere mag ≥ 6 order by time · limit 20 → 5 rows (M7.8 01:17, M7.5 10:24, M6.7, M6.4 Hatay …)
  5. Five events above M6. I'll open on the M7.8 at Pazarcık with the fault trace, cut to the M7.5 nine hours later, then step the time filter week by week and close on Hatay. Writing chapters.

  6. create_story_chapter × 6each with a beat: camera + layerStates.filter (time ≤ chapter end) + highlight (mag ≥ 6) + transition fly 1.6 s
  7. publish_share_linkvisibility: unlisted → https://spatly.io/s/kmr-2023-aft
  8. Done. Six chapters, unlisted link, 4,812 features stay live from the USGS feed. Chapter 4 could use a pull quote. Want me to draft one from the viewport stats?

07Spatly Marks

An original visual language. Sold, not decorated.

128 UI icons, 22 map markers, atlas ornaments, chart marks and animated live affordances, all on one 24px grid with a 1.5px stroke and one recurring survey tick. Marks follow your theme tokens, load into MapLibre as SDF images, and export as an SVG sprite + React components for your own apps.

Marks documentation
UI icons · 24 grid128
layers
layer-add
beat
beat-save
camera
compass
crs
live
filter
geofence
elevation
terrain
routing
pin
line
area
callout
tour
north
scale
legend
pitch
bearing
globe
map
marks
insight
publish
share
embed
story
slides
dashboard
chapter
slide-add
widget
kpi
template
presenter
laser
notes
bookmark
text
image
video
quote
table
list
chart-bar
chart-line
chart-pie
type
palette
ramp
align-left
align-center
align-right
align-justify
align-top
align-middle
align-bottom
grid
frame
group
component
comment
time
play
pause
refresh
upload
download
link
database
api
webhook
mcp
agent
history
stale
offline
warning
info
password
lock-domain
plus
minus
close
check
chevron-down
chevron-up
chevron-left
chevron-right
arrow-up
arrow-down
arrow-left
arrow-right
drag
search
zoom-in
zoom-out
fit
fullscreen
expand
collapse
external
eye
eye-off
lock
unlock
duplicate
copy
trash
undo
redo
code
settings
spark
magic
user
workspace
mobile
tablet
desktop
wall
sun
moon
ops
Map markers · 32 grid · SDF-ready22
Live affordances · ornaments · logo
pulse
scan
flow
stale
offline
N
02.55 km
Legend
M ≥ 6 aftershock
Spatlya beat on a plate
08Bring your own data

Power users bring PostGIS habits. Beginners bring a spreadsheet.

Drop a file and Spatly ingests it into PostGIS, samples field stats for ramps and categories, and serves it as GeoJSON or vector tiles depending on size. A CSV with an address column gets geocoded on the way in. Every source keeps its refresh policy, cache and health.

≤ 5k features
GeoJSON endpoint
> 5k features
ST_AsMVT tiles
≤ 500 rows
auto-geocode
GeoJSONShapefile (.zip)CSV / XYKMLGPXPMTilesVector tilesWMS / WMTSFeature services (REST)REST APIWebhookMCP source
Data library: every dataset in the workspace with geometry type, feature count, fields and refresh policy
Data librarydatasets · connections · live sources
A live binding, anatomised
sourcehttps://earthquake.usgs.gov/…/all_day.geojson
refreshinterval · 60 s · ETag
cacheserver-side · shared by all viewers
last success12:04:31 · 212 ms
schema mapmag → measure · time → datetime
error surfacehatched widget + e-mail after 3 failures
10Pricing

Free to publish. Pay when you outgrow three projects.

All four surfaces, MCP and embeds on every plan. Paid plans add room, members, a custom domain and remove the badge.

Full comparison
Free
$0forever
  • 3 projects · 10 datasets
  • 250 MB storage · 2 live bindings
  • 1 member
  • Custom domain
  • Remove “Made with Spatly”
Start free
ProMost chosen
$29per month · $190 / yr
  • 50 projects · 200 datasets
  • 10 GB storage · 25 live bindings
  • 3 members
  • Custom domain
  • Remove “Made with Spatly”
Start Pro
Team
$79per month · $590 / yr
  • 500 projects · 2000 datasets
  • 100 GB storage · 200 live bindings
  • 25 members
  • Custom domain
  • Remove “Made with Spatly”
Start Team

Drop a GeoJSON. Get a story in ten minutes.

Wire a live KPI without code. Embed the insight anywhere. Let your agent do it for you.

41.0082°N 28.9784°E · z 11.3 · ⟂ 0°