InkStone

Canvas

Contents

InkStone can publish Obsidian .canvas files as read-only visual boards. Nodes are rendered as positioned boxes, edges as SVG bezier curves with direction arrows, and edge labels as HTML overlays — all scaled to fit the page width.


Publishing a canvas

The recommended way to publish a canvas is the filename marker: rename the file so it ends with __website before the .canvas extension.

My Diagram__website.canvas

The page title is everything before __website — in this case "My Diagram". The canvas is served at the same URL that an .md file in the same folder would produce.

Why the filename marker?

Obsidian strips unrecognised top-level JSON keys when it re-saves a canvas. That means "website": true disappears the next time you edit the file in Obsidian. The filename marker survives re-saves because Obsidian never touches the filename.

Top-level JSON fields

These optional JSON fields are read from the canvas file:

Field Purpose
"title": "..." Overrides the filename-derived title
"date": "YYYY-MM-DD" Publication date
"summary": "..." Shown on listing cards
"tags": [...] Content tags
"featured": true Show in the Featured section of the parent listing
"banner": "..." Hero image URL

Legacy: "website": true JSON flag

Adding "website": true to the canvas JSON still works as a fallback, but it is fragile — Obsidian will remove it on the next re-save. Use the filename marker instead.


Node types

All four Obsidian node types are supported:

Type Rendered as
text Box with rendered inline markdown (bold, italic, inline code, line breaks)
file Card with a scrollable preview of the linked note, or inline media — see below
link Box with a clickable external URL (opens in a new tab)
group Dashed-border container rendered beneath other nodes; label appears on the top edge

Text nodes

Text content supports a subset of inline markdown:

File nodes

File nodes behave differently depending on what they point at:

Published post — if the file resolves to a published InkStone post, the node renders as a card with: - A clickable header showing the post title (links to the post URL) - A scrollable preview of the full rendered note body

Vault image, video, or audio — if the file points at a media file inside the vault (.jpg, .png, .gif, .webp, .svg, .mp4, .webm, .mov, .mp3, .ogg, .wav, .flac, .m4a), the media is embedded inline inside the card.

Unresolved file — if the file is not a published post or recognised media, the node shows the filename as plain text.

Node colours

Obsidian's six preset colours are supported:

Value Colour
"1" Red
"2" Orange
"3" Yellow
"4" Green
"5" Cyan
"6" Purple

The colour is applied as the node's border colour. Unset nodes use the default theme border.


Edges

Edges are drawn as SVG bezier curves with a direction arrow at the target end. The arrowhead colour matches the edge stroke.

The control-point length scales with the distance between connected sides so curves stay smooth at any layout.

Edge labels

If an edge has a "label" field, the label renders as a small HTML badge at the visual midpoint of the curve — not as SVG text, which would be distorted by the aspect-ratio scaling. The badge has a background so it stays readable over node boxes.

Edge colours

Edge colour follows the same six-value scheme as node colours. Edges without a "color" use the theme's muted text colour.


Sizing and layout

The canvas is rendered at the full width of the article column. The height is set automatically to preserve the original aspect ratio of all nodes combined (plus padding). Nodes are positioned with percentage coordinates so the layout stays correct at any viewport width.

Size nodes to fit their content

Canvas nodes have fixed sizes defined by you in Obsidian. If a text node is too small for its content, the overflow is hidden (with a subtle fade). Resize nodes in Obsidian until the content fits — what you see in Obsidian is what gets published.


Example

A minimal two-node canvas (save as Simple Diagram__website.canvas):

{
  "title": "Simple Diagram",
  "nodes": [
    {
      "id": "a",
      "type": "text",
      "text": "**Start**",
      "x": -200, "y": -60,
      "width": 160, "height": 80,
      "color": "5"
    },
    {
      "id": "b",
      "type": "text",
      "text": "**End**",
      "x": 80, "y": -60,
      "width": 160, "height": 80,
      "color": "4"
    }
  ],
  "edges": [
    {
      "id": "e1",
      "fromNode": "a", "fromSide": "right",
      "toNode": "b",   "toSide": "left",
      "label": "next"
    }
  ]
}

See also