Hexcrawl

approved

by Vasilii Negrebetskii

Render zoomable, pannable hexcrawl maps from a folder of hex notes via a `hexcrawl` code block. - This plugin has not been manually reviewed by Obsidian staff.

37 downloadsUpdated 2d agoMIT

Obsidian Hexcrawl

Release Downloads Ko-fi

Turn a folder of notes into a fully interactive, easily editable hex map. Drop a hexcrawl code block into any note, point it at a folder, and every note with hex-q/hex-r frontmatter becomes a hex on the map. The map itself isn't a static picture: paint terrain, drop icons, and draw paths straight onto it with the built-in toolbar.

Example hexcrawl

Features

  • Notes as hexes — coordinates live in frontmatter; click a hex to open its note.

  • No fixed schema — add whatever properties your campaign needs instead of being boxed into a predefined set of fields.

  • No bespoke views — browse, filter, or query hexes with the tools you already use (Bases, Dataview, search).

  • Pan & zoom — scroll to zoom on the cursor, drag to pan, auto-fit on load.

  • Editing toolbar — fill terrain, place icons, draw paths directly on the map, and more.

  • Customizable visuals — load third-party icon packs of your choice, define various terrain palettes, configure as many path types as you need.

    A couple of great similar plugins exist for Obsidian too: Hexmap World Creator, Hex Cartographer, Text Mapper. They're more mature, and may fit your use case better. However, Hexcrawl aims to stand out by focusing on flexibility of use and visuals, keeping things simple and staying close to Obsidian's own primitives.

Quick start

```hexcrawl
folder: Hexes
orientation: pointy
cols: 10
rows: 8
```

And a hex note anywhere in that folder:

---
hex-q: 3 # column (0-based)
hex-r: 2 # row (0-based)
hex-terrain: forest
---

Reference

Editing on the map

A toolbar pinned to the top-right corner of every rendered map lets you edit it directly, with no frontmatter to hand-write:

ToolWhat it does
BrushPaint a terrain onto hexes one click at a time; pick "Eraser" to clear it.
BucketFlood-fill connected same-terrain hexes with a new one.
IconDrop an icon from the palette's icon folder onto a hex, or erase it.
GM IconSame as Icon, but for hex-gm-icon — its own layer, always rendered above everything else.
PathDraw a new road/river by clicking hexes in order, or click an existing path to move, add, or remove its points.
BorderDraw a new border/barrier by clicking near a shared hex edge to start it, or click an existing border to extend either end (via the add-edge dots) or right-click an end edge to remove it.
LayersToggle terrain, borders, paths, icons, and GM icons on or off independently.

Every change is written straight to the affected note's frontmatter (or creates a new path note), so the map and the notes never drift apart.

Code block options

KeyRequiredDefaultDescription
folderyesVault-relative path to the folder containing hex notes (scanned non-recursively).
colsyesNumber of columns to render.
rowsyesNumber of rows to render.
orientationnopointypointy or flat. Pointy-top hexes stagger by row; flat-top hexes stagger by column.
staggernooddWhich row (pointy) or column (flat) is the shifted one.
hexSizeno40Hex radius (center to vertex) in pixels.
heightno500Height of the visible map panel, in pixels. Width fills the note.
coordsnofalseShow q/r coordinate labels along the top and left axes.
palettenodefault global paletteName of a global palette (e.g. palette: Wikipedia), or an inline mapping for a one-off palette scoped to this block.
pathsnoVault-relative path to a folder of path notes (roads, rivers, ...).
bordersnoVault-relative path to a folder of border notes (barriers, walls, ...).
gmIconModenodefaultHow hex-gm-icon is rendered: default (hex center, like a regular icon) or mini (half-size, top-left of the hex).

Palettes

Manage named palettes vault-wide from Settings → Hexcrawl. Add, duplicate, delete, mark a default, and edit each one's terrain/path/border entries and icons folder (with a picker and live preview). Leave a palette's icons folder empty to use the plugin's bundled icon pack; set one to use only icons from that vault folder instead — the two are never combined.

A hexcrawl block picks one with palette: <name>, or omits it to use the default.

The same shape also works inline, scoped to a single block:

palette:
  icons: TTRPG/Icons
  terrain:
    forest:
      color: "#3F7A52"
      icon: forest
    hills:
      color: "#D68F4E"
  paths:
    road:
      color: "#A9895F"
      dash: dashed
    river:
      color: "#4A90C2"
      width: 4
      spline: true
  borders:
    barrier:
      color: "#f50000"
      width: 4
      dash: solid
Terrain keyRequiredDescription
colornoHex fill color.
iconnoFilename basename (no extension) looked up in the palette's icons folder, or the bundled icon pack if none is set.
Path keyRequiredDefaultDescription
colornovar(--text-muted)Stroke color.
widthno3Stroke width in pixels.
dashnosolidsolid, dashed, or dotted.
splinenofalseSmooth curve vs. straight segments, unless a note overrides it.
Border keyRequiredDefaultDescription
colornovar(--text-muted)Stroke color.
widthno3Stroke width in pixels.
dashnosolidsolid, dashed, or dotted.
edgeOffsetno0Pixels the edge is shifted toward the first hex of each pair — for building two-sided borders.

hex-icon on a note always wins over the palette's icon. A hex-terrain, path-type, or border-type with no palette match falls back to being used directly as a CSS color.

Hex note frontmatter

Frontmatter keyRequiredDescription
hex-qyesColumn coordinate (integer).
hex-ryesRow coordinate (integer).
hex-terrainnoLooked up against the palette's terrain names; falls back to a literal CSS color (e.g. #4a7c3f, green) if no match. Omit for an uncolored hex.
hex-iconnoIcon basename (no extension) from the active palette's icons folder (or its bundled pack, if none is set). Overrides the palette's own icon for hex-terrain.
hex-gm-iconnoIcon basename, looked up the same way as hex-icon. Rendered on its own layer, above everything else, per the block's gmIconMode.

A note missing or non-integer hex-q/hex-r is ignored.

Paths (roads, rivers, etc)

Each path is its own note in the block's paths folder. The note's title doubles as its name unless path-name is set:

---
path-type: road
path-hexes:
  - [2, 3]
  - [3, 3]
  - [3, 4]
path-spline: true
---
Frontmatter keyRequiredDescription
path-hexesyesOrdered list of [q, r] pairs (at least 2). The path is drawn through each hex's center, in order.
path-typenoLooked up against the palette's path types; also applied as a CSS class (hexcrawl-path-{type}) for custom styling.
path-splinenoOverrides the type's default curve/straight rendering for this path.

Borders (barriers, walls, etc)

Unlike a path, a border isn't drawn through hex centers — it runs along the shared edges between neighboring hexes, which is why border-hexes is a list of hex pairs rather than a flat list: each pair names the two hexes on either side of one edge. Consecutive pairs must share exactly one hex, head-to-tail, with the previous pair — this is also what lets a border turn a corner around a single hex, pivoting on it for two edges in a row. Each note is its own border, in the block's borders folder, with its title doubling as its name:

---
border-type: barrier
border-hexes:
  - [[0, 0], [1, 1]]
  - [[1, 1], [0, 1]]
---
Frontmatter keyRequiredDescription
border-hexesyesOrdered list of hex pairs (at least 1). Each pair's two hexes must be neighbors, and each pair must continue head-to-tail from the previous one.
border-typenoLooked up against the palette's border types; also applied as a CSS class (hexcrawl-border-{type}) for custom styling.

A pair may name a hex just outside the grid — that's how a border caps the map's outer boundary, rather than a sign the border is invalid. A border with any pair failing validation (not neighbors, not head-to-tail, or entirely off-grid) is skipped on load, with an error logged and shown as a Notice.

Only the first or last edge of a border can be removed (via right-click, in the Border tool); removing the only remaining edge deletes the whole note.

Contributing

Contributions via bug reports, bug fixes, documentation, and general improvements are always welcome. For more major feature work, make an issue about the feature idea / reach out to me so we can judge feasibility and how best to implement it.

Local development

npm install
npm run dev            # watch build

Preparing for creating pull requests

If you plan on doing pull request, we would also recommend to do the following in advance of creating the pull request:

npm run build          # production build (type-checks first)
npm test               # run unit tests
npm run lint           # eslint
npm run format:check   # prettier --check

Install the pre-commit hook once per clone so typecheck/lint/format issues are caught before you push (same checks CI runs):

pre-commit install

Inspiration & Credits

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.