Loam is free and open source for macOS. Your notes stay plain Markdown files on your disk. Download for macOS.[Download for macOS.]
All pages

Notes

Boards

.md

Arrange notes, text, links and groups on a whiteboard joined by arrows, in a JSON Canvas file that Obsidian also reads.

A board is a whiteboard of cards: notes, text, web links and groups, joined by arrows. A card that shows a note stores only the note's path, so the note stays one note. When you move or rename it, every board that shows it follows.

Add cards and arrows

  • Double-click the board, or press T, to write a text card.
  • Press N to add a note as a card. You can also drag notes in from the sidebar.
  • Drag from a dot on a card's side onto another card to draw an arrow. Drop it on empty space to create a new card.
  • Press S for a sticky note, or P for the pen.
  • Press F to frame the selection as a slide.

Build a board from the CLI

  1. Create an empty board. The command prints its path.
  2. Add a card that shows a note. The command prints the card's id.
  3. Add a text card.
  4. Connect two cards with an arrow, using their ids.
  5. Read the board back as JSON Canvas.
loam --vault ~/Notes canvas.new folder=Boards title=Trip
loam --vault ~/Notes canvas.add-node path=Boards/Trip.canvas file=Work/Launch.md x=320 y=0 width=400 height=300
loam --vault ~/Notes canvas.add-node path=Boards/Trip.canvas text="**Dates**: May" x=0 y=0 width=260 height=80
loam --vault ~/Notes canvas.connect path=Boards/Trip.canvas from=<id> to=<id> label=when
loam --vault ~/Notes canvas.read path=Boards/Trip.canvas

Replace each <id> with an id that canvas.add-node returned.

Frames and slides

A frame is a 16:9 slide. The first frame starts the presentation. In the app, press Option+Cmd+P to present the frames one by one.

loam --vault ~/Notes canvas.add-frame path=Boards/Trip.canvas x=1500 y=0 label="Slide 2"
loam --vault ~/Notes canvas.slides path=Boards/Trip.canvas

Commands

CommandArgumentsWhat it does
canvas.newfolder (default Inbox), title (default Board)Creates an empty board and returns its path.
canvas.listpath (optional)Lists every board, or only the boards that show that note.
canvas.readpathReturns the board as JSON Canvas.
canvas.add-nodepath; text, file, url, or type=group with label; optional color, x, y, width, height, nearAdds a card and returns its id.
canvas.update-nodepath, id; optional text, file, url, label, color, fontSize, shape, border, textAlign, x, y, width, heightChanges a card. An empty value removes text, label, color, fontSize, shape, border or textAlign.
canvas.connectpath, from, to; optional label, color, fromSide, toSide, shape, dashedAdds an arrow and returns its id.
canvas.alignpath, ids; align or distributeLines cards up, or spaces them evenly.
canvas.removepath, idsRemoves cards or arrows by id. The notes stay.
canvas.add-framepath; ids, or x and y; optional width, labelAdds a 16:9 frame and returns its id.
canvas.slidespathLists the slides in presentation order.
For AI agents: loam list shows the canvas.* commands with their arguments.