Skip to content

Most moodboard software stores a board's layout somewhere you can't get at directly — a row in a database, a blob behind a sync service, a format that has to be exported before any other tool can look at it. Version control never gets a foothold there, because there's nothing readable to version. A .coii-canvas doesn't have that problem: it's pretty-printed JSON, sitting in a folder you already control, and every plate's position, every note, every tag reference is a line in a text file the moment you open it in anything that reads text. That's the whole reason the steps below work at all — they're describing how to point an ordinary version control tool at a file that was never hidden from it in the first place.

This isn't a feature the app adds. It's a consequence of the canvas format being a plain file rather than a database row, and it's worth treating deliberately rather than as an afterthought once a board has already gone through several rounds of changes nobody can reconstruct.

It's also worth being clear about what "version control" means here versus what it might mean for the images themselves. This page is about the canvas — the arrangement, the notes, which plates are on the board and where — not about tracking edits to the photographs and clips a board references. Building a moodboard from folders you already have covers how those images get onto a board in the first place; this page picks up once a board exists and starts changing over time.

Step 1: Put the canvas somewhere a repository can see it

A canvas file can live anywhere inside the folder you've registered as a workspace, so the first real decision is where — a dedicated Boards folder is a reasonable default, especially if you plan to track just the canvases and not every image in the same repository.

~/Projects/Atlas
  • Concept art
  • Client refs
  • Boards
  • coii-thumbnailswritten by Coii Ref
  • coii-proxieswritten by Coii Ref
  • coii.dbwritten by Coii Ref
Boards kept in their own folder inside the workspace, separate from the source images — a smaller, more focused thing to put under version control.

None of the steps below require the workspace itself to know anything about version control, and that independence runs both directions: deleting the repository entirely, at any point, leaves the workspace exactly as functional as it was before — the canvas still opens, the board still renders, and nothing about how the app reads the file changes. Version control here is purely additive, a history layered on top of a file that was already going to exist regardless of whether anyone chose to track it.

Step 2: Initialize a repository and make the first commit

From inside that folder, git init and a first commit of the canvas file is the entire setup. Nothing about the app needs to know a repository exists alongside it — the workspace watcher reads whatever's on disk regardless of whether it's also tracked by git, so version control and the app's own indexing run independently of each other without conflicting.

Step 3: Decide what belongs in the diff and what doesn't

A workspace folder holds more than the canvas: a database, thumbnails and canvas-resolution proxies all live beside your files. None of those are meant to be read by a person, and none of them diff usefully — a thumbnail that changed is a different binary blob, not a readable change. Excluding coii.db, coii-thumbnails and coii-proxies from the repository, the same way build artifacts are usually excluded from any other project, keeps the history focused on the one file that's actually worth tracking changes to.

Whether the source images themselves belong in the same repository is a separate call, and a reasonable one to answer "no" to — a repository full of full-resolution photographs grows large fast, and the images are usually already backed up by whatever already backs up the rest of the folder. Tracking the canvas on its own, with the images left to that other backup, covers the thing that actually benefits from a diff-based history without duplicating a job something else already does.

Step 4: Read a diff the way you would for any text file

This is the part that makes the whole exercise worth doing. A diff between two commits of a canvas shows exactly which plate entries were added, removed, repositioned or renamed, because each plate is its own block in the JSON rather than a pixel in a flattened image. A change that moved three plates and added a note reads as three moved blocks and one new line — legible in an ordinary diff view, the same way a change to a paragraph of prose would be.

atlas-season-palette-v3.coii-canvas
Version three of a board that's gone through two rounds of client notes — each round a commit, each commit a diff a person can actually read.

That readability is the entire argument for doing this over just saving board-final-v2-actually-final.coii-canvas next to the original and hoping the filenames stay meaningful. A version history answers "what changed between Tuesday and today" with an actual diff instead of a naming convention that degrades the moment two people are both saving copies.

Step 5: Roll back to an earlier version when a change needs undoing

Because the app re-reads the canvas file rather than keeping its own internal undo history, checking out an earlier commit and reopening the file is enough to see the board exactly as it stood at that point — there's no separate "restore" step inside the app itself, since from its perspective the file on disk simply changed, the same way it would if you'd edited it by hand.

That progression from round-by-round commits to a tagged history is worth building as a habit rather than a one-off exercise applied to a single troublesome board — the second and third boards under this treatment take less thought than the first, once committing after every meaningful change becomes as automatic as saving the file itself.

Step 6: Branch a board when two versions need to exist at once

A client-facing variant of a board and an internal working version are a natural fit for a branch each, the same as any other file that needs to diverge and possibly merge back later. Because plates are addressable blocks rather than pixels, a JSON-aware merge tool can often reconcile two branches that touched different plates without a person resolving every line by hand — though a merge that touched the same plate on both sides still needs a human decision, the same as any other conflicting edit.

What doesn't version well, and why that's fine

A canvas versions cleanly because it's small, textual and changes in discrete, nameable ways — a plate moved, a note added. The images it references don't share those properties: a full-resolution photograph is large, binary and, once captured, essentially unchanging, which is exactly the profile of a file that gains nothing from being tracked by a tool built around line-by-line text diffs. Putting thousands of unchanging images into the same repository as a frequently-edited canvas mixes a file that benefits enormously from version control with thousands that would benefit from it not at all, and grows the repository by orders of magnitude for no real gain. Leaving the images to whatever already backs up the folder, and reserving the repository for the one file where a diff is actually legible, is the version of this that scales rather than the version that technically tracks everything.

Step 7: Tag a commit for each round a client actually saw

A running history of every small nudge is useful, but the moments that matter most later are the ones a client actually reviewed — "this is what they approved," specifically, as opposed to every intermediate save along the way. Tagging the commit at the end of each review round, the way a piece of software gets tagged at a release, turns "what did round two look like" from a scroll through history into a single lookup.

A worked example: what three rounds of client notes actually looks like

Round one is the first commit — a dozen plates, a rough arrangement, tagged round-1. The client asks for two images swapped and the whole thing nudged tighter; that's a second commit, and the diff between it and round-1 shows exactly those two plate entries changing and nothing else — no ambiguity about whether anything unrelated moved, because nothing else is in the diff. Tag that one round-2.

Round three drops four plates entirely and adds six new ones after the client changes direction on the location. The diff here is larger, but still exactly as legible: six new blocks, four removed, the rest untouched. Anyone looking back at this project in six months — to reuse the approach on a similar job, or to settle a dispute about who approved what — can check out round-1, round-2 or the current state and see precisely what each one contained, rather than relying on anyone's memory of which version was which.

That's the entire value of the exercise laid out concretely: not a backup, not a collaboration feature, just an accurate record of a board's own history that a diff tool already knows how to read.

Finding the boards worth bringing under version control

Not every canvas needs this treatment — a scratch board thrown together for one afternoon's work doesn't need a commit history any more than a sketch does. What usually does are the boards that get revisited: ongoing client work, a project that spans months, anything that's already been renamed -v2 once.

/type:image tag:has-canvas in:~/Projects added:<2026-06-01
typeimagetaghas-canvasin~/Projectsadded<2026-06-01
34 items
Older project folders worth checking for a canvas that's still being revised by hand rather than tracked — the ones most likely to benefit from this.

A tag applied to a project's folder once a canvas exists inside it makes this kind of sweep straightforward later, rather than having to remember by project which boards are the ones actually worth versioning.

Common mistakes worth avoiding

Committing the entire workspace folder wholesale is the most common one — it works, technically, but buries the one file worth diffing under thumbnails and a database that change on every re-index and add nothing readable to the history. A quieter mistake is treating the repository as a backup rather than a history: version control answers "what changed and when," not "is this safe if the drive dies," and the two problems need different tools even though they can live in the same folder without conflict. And waiting until a board has already gone through a dozen unversioned edits before starting — the first commit is cheapest the day a canvas is created, not after months of manual -final filenames have already lost the story a real history would have kept.

What to do as the number of boards grows

One repository per project, rather than one for the entire workspace, keeps history focused and keeps a merge conflict on one client's boards from ever touching another's. That maps naturally onto organizing references by project in the first place — a project that already has its own folder gets its own repository inside it for free, rather than needing a separate scheme invented just for version control. For someone managing boards across many projects at once, a consistent Boards folder name inside each project root — the same convention step one sets up — makes it straightforward to script initializing a repository as part of starting a new project, rather than deciding folder-by-folder whether this one gets tracked.

None of this requires anything from the app beyond writing the canvas as plain, readable JSON in the first place — the version control itself is entirely tools you likely already have installed, applied to a file that's already what a moodboard is rather than anything the app had to add for this to work. Sharing a canvas without a cloud account covers the companion problem of getting a board to someone else once it's in the state you want, and backing up a reference library properly covers the wider folder this all sits inside. What a sidecar file is and an infinite canvas for reference images are worth reading if the plain-file argument above is the part that actually matters to you. Or try it against a board you're already revising — thirty days, every feature, no card and no account, is enough to put a real canvas under version control and watch the first few diffs actually make sense.

Questions

Do I need special software to version a canvas?
No — a .coii-canvas is plain JSON, so an ordinary version control tool like git reads and diffs it the same way it would any text file, with no export or conversion step first.
Should the images on the board go into the same repository?
That's your call, not a requirement of the format. Many people keep the canvas under version control and leave large image files out of the repo, tracked instead by whatever already backs up the folder.
Can I see exactly which plates changed between two versions?
Yes — a JSON diff between two commits shows which plate entries were added, removed or moved, the same way a diff shows changed lines in any other text file.
What if I want to go back to how a board looked last month?
Check out the earlier commit of the canvas file. Since the app re-reads the file rather than keeping its own internal history, the board opens exactly as it was at that point.