https://obsidian.md/blog/json-canvas/
We just released it today, so this is still a very nascent project. A little over a year ago we released Obsidian Canvas. The .canvas file format has felt stable enough to give it a name and resources that other apps can freely use. See the original Show HN:
https://news.ycombinator.com/item?id=34066824
The spec is conservative, and definitely does not support many features canvas apps will want to implement (yet).
The purpose of giving JSON Canvas a name and site is to encourage an interoperable ecosystem to grow around this format. We're definitely looking for feedback of all kinds!
It's great to see all the suggestions already shared in this thread because it starts to provide a roadmap for how this could become a more useful format for other apps.
Which existing formats were considered before building your own, e.g. SVG/Excalidraw/draw.io/...?
I built a library at AWS for a general canvas editor called Diagram Maker. It recently got archived so I stood up a fork here: https://github.com/sameergoyal/diagrammer and the data format we use is strikingly similar. Check it out here: https://sameergoyal.github.io/diagrammer/?path=/docs/docs-us.... The key differences are panels, workspace & editor.
I dont actively work on the project outside of bugs, but maybe there are ways to collaborate here, like moving my project to use & extend the JSON canvas spec.
JSON Canvas seems to follow in that spirit by being very lightweight, so a lot of implementation details (i.e. how are files rendered, what file formats are supported, etc), edit tags, etc) are left open to implementation.
Markdown and JSON are meant to be non-opague file formats that prioritizes portability and human readability over other features. An application format like Sqlite has a lot of benefits over markdown, but it loses the benefits of text based formats like being compatible with git and is less portable.
What I would like to see is a convention for extending the node and edge definitions, similar to frontmatter in markdown files- something that is not required for basic rendering but is a nice-to-have for applications to consume) - that way portability between apps of varying complexity can be maximized while still allowing for more complex features that some apps might implement. Markdown has the benefit of supporting extensions (for example like tables in GFM) - apps that are not compatible can still render the unsupported markup. But there should be an explicit way to extend open JSON formats.
Some feedback off the top of my head and from reading the comments:
1. *Specifying node ordering*. Obsidian seems to just send whatever is last touched to the top, but this makes a common feature in many apps (move up/down/back/front) more difficult to impement
2.*More explicit definition for line shape*. Adding a way to "bend" a line in a specific way. Useful for creating more complex diagrams.
3. *Relations between nodes*. Group nodes contain child nodes, but the spec doesn't specify how the child nodes are defined. I would expect it to have a `children` property to nest nodes. Obsidian seems to implicitly link nodes to groups based on whether their bounds intersect. This makes it difficult to implement some common features:
a. nodes that exist outside of the bounds of its group, for example a node that "floats" just outside of the edge of the group's borders.
b. nodes that are not part of a group even though it exists within the bounds of that group.
There are many different ways for a canvas app to extend the spec to implement those features, but it seems like something that should be defined in the spec to maximize portability
4. *Extensibility.* Either explicitly support or provide a standard for defining more styles for nodes and edges, such as stroke width, stroke style, rotation, etc. It seems like "color" should be a part of this as well, rather than being an explicit property of a node.
5. *Embeds.* Supporting "embeds" as a node type. I even think the "file" node should be redefined as `embed` with a `uri` property to support different schemes (`file://`, `oembed://`, `https://`) and maybe a `mime-type` (`text/markdown`, `image/webp`). The file node's "subpath" property seems to be only relevant for markdown files, so I think that should be an extension rather than an explicitly defined.
6.*YAML* :) (Should just seemlessly convert from json, but yaml is more readable than json)
Being able to design standards that evolve over time and making tough decisions about what what to make explicit and what to leave implicit is a skill I want to improve on as a developer this year. Does anyone have any resource recommendations or best practices to recommend for me to research?
Please don't, it has one of the most confusing syntax out there with lists and maps, and it won't do well for parsing.
I don't think human readability is a critical feature of JSON at this point. If that's your priority, you can use YAML. Readable JSON is nice because for small files you can read or edit small sections of it, and it's easy to debug when manipulating it with machine code. But there are plenty of cases where a huge JSON file is still useful even if it's barely human readable.
My heuristic has always been: use YAML if you expect humans to create the file (or maintain large chunks of it), otherwise use JSON. For example, Kubernetes config is YAML because humans create it from scratch, and it would suck to do that with JSON. Whereas package.json is JSON because machine code initializes it and humans only make minor edits to specific fields.
In the case of this canvas format, I wouldn't expect humans to create the file from scratch, so use JSON over YAML. Then the question is, will humans even care about reading the raw JSON? Probably not. So why not use something like SQLite or Protobuf? The most compelling reason would be that humans writing code to interface with the format can use parsing tools from their language's standard library.
The purpose of a spec is to specify, and if you don’t specify and leave things open to interpretation, then that completely defeats the purpose.
Anybody who’s worked with a poorly defined spec knows exactly how bad this can be. A good example would be the shambles that is the HL7 spec used in healthcare.
A former colleague had a phrase for this: “once you’ve seen one HL7 message… you’ve seen one HL7 message”. Which really highlights the issue of a standard that’s open to interpretation.
The issues raised (in the comments here) seem to hint at a lack of specificity. That is something that they should really look at improving.
I think overall any group that tries to come up with a standard that can unify a field should be lauded and supported. But perhaps calling this a 0.1 release, and taking the feedback on board, would be the best way forward.
What Markdown got right was creating a nicely readable lightweight markup syntax.
And Markdown also demonstrated how to create a bad precedence for future consolidation by being so loosey goosey and underspecified (and with a bad reference implementation). That there is a Commonmark at all is solely because of others picking up the slack and doing the unthankful gruntwork of creating 100 if-then-else statements in a semi-formal prose format.
- How do coordinates work? Does +Y point up (OpenGL) or down (web)? Is the origin meaningful? What are the units - how does this interact with font sizes? High-DPI displays?
- What’s the difference between a file node and a URL node linking to file://./? Are files supposed to be transcluded? What filetypes are allowed? The home page seems to have an image — was this done using a file node or a markdown node with an <img /> element?
- What HTML tags are allowable in markdown? Is JavaScript allowed?
- Why does the group node allow a background image? If both image and color are specified, which takes precedence? How are children of the group specified?
A couple feature requests for extensibility or interoperability with Excalidraw and TLDRaw and friends: drawings / scribbles, predefined shapes like circle or rectangles, ability to specify fill style, edge width, transparency, ability to embed images, more detailed placement for connector start/end points, etc.
I assume no.
> What are the units
Arbitrary. The website says pixels, but the demo lets you zoom in and out, so I think defining the unit as pixels is pretty meaningless, except as a hint to the viewer of the initial scale for the canvas. Even then I can see good reasons for a viewer program to just ignore that and use whatever initial scale allows everything to fit on screen.
> how does this interact with font sizes?
Seems like the font size can't be changed, but I'd imagine it's a specific number of units.
> High-DPI displays?
Not sure what kind of answer you're looking for here. You can just scale everything, so support for High-DPI displays would be up to the viewer program.
I assume yes, as it makes placement of things much easier if they're all relative to a known origin. The limits of the canvas would be "realistically infinite" in all directions though I assume...
If using integers, I'd cap it at 2^53 to align with js's max safe integer which I think is just a double.
This is a data format. This is an abstract data model.
So the spec doesn't specify rendering.
Rendering could however be specified separately (and its reference implementation is Obsidian).
This strikes me as exactly the type of application data that would benefit from being represented in SQLite. Of course, JSON is a `JSON.parse` away, but now you're building your own...everything else. Storage/retrieval, validation, querying/filtering/indexing, graph traversal, etc. It's all yours.
There's so many benefits to building this kind of thing in SQLite. You get data validation, atomic transactions in memory and on disk, a high-level query interface, lazy loading (i.e., only load nodes at most 2 edges away), triggers (when I delete this node, automatically delete edges that point to it), and a stable on-disk format, to say nothing of the fact that SQLite itself is just about the most stable software there is.
By the way, no disrespect to JSON Canvas, it looks like good work, just trying to offer the perspective of someone who has done stuff like this in the past.
Interacting with SQLite from different programming languages is easier than most other formats, but you still need a SQLite binding. They're available for every language but that's still a not-completely-trivial dependency.
I expect most tools that people build against JSON Canvas will run in a web browser. Adding SQLite as a dependency means you need SQLite running in WebAssembly - totally possible, and even officially supported these days (the SQLite team run their own WASM builds now) but still a sizable piece of extra complexity over just using JSON.parse(...)
Also: SQLite files aren't very easy to diff, so they're not great for collaboration in version control. JSON is better for that.
I'm 100% with you on the schema changes and versioning challenge. The best way to address this IMO would be for the spec to include a top-level "version": key which indicates the version of the spec that a file was created against.
Handled carefully and introduced right at the start of the project this could ensure an ecosystem grows up around the standard such that older spec versions can always be opened by newer implementations, and any implementation can fail-fast if it is given a file that it doesn't yet know how to handle.
> Also: SQLite files aren't very easy to diff, so they're not great for collaboration in version control. JSON is better for that.
Yeah, if we're talking about diffing the literal file itself, then that changes things. At that point, we're not just talking about a storage format, we're talking about interchange as well. In that case I'd ask - of course this is application specific, not general - what data are we putting on the wire? Where does that data live?
For example, if the main state state lives in a your browser instance, and you ship updates (i.e., "CREATE", "EDIT", "DELETE" or some such) back and forth between collaborators, then diffing the state of whatever you have is fairly easy, SQLite or JSON or whatever else. But if we're shipping the actual file itself over the wire or attempting to version control it, then you're absolutely right and diffing the SQLite file is inferior.
There are some interesting tradeoffs in this space. This is a fun discussion!
Adding C sqlite to a golang project adds a significant hit to build times and cross-compilation/static linking complexity[0]. When I looked into the native Go implementations of sqlite I came away with the feeling it wasn't worth the tradeoffs compared to using the C version, but now I still have to deal with the issues above.
I haven't looked deeply into how sqlite works, but my instincts tell me the reason we don't have high quality implementations in every language is because it's actually too complex to treat as a protocol.
I would love to see something fill the void between plain text and sqlite.
I agree that going from text to sqlite is a bit of a hurdle, especially if you're not writing C :)
If you want to improve on JSON, you would have to go into an other direction. Maybe something like postgis would be helpful for extremely large canvasses.
JSON Schema is pretty powerful by the way. Checkout the documentation. SQLite is absolutely no match there. What Sqlite could bring is speed, but I dont see how in the contxt of canvas it would be of any help here.
There is a somewhat recent STRICT mode that strengthens them: https://www.sqlite.org/stricttables.html
SQLite as an Application File Format, https://sqlite.org/appfileformat.html
Sure, they could screw me over and start charging absurd amounts of money for their app, but high quality open source alternatives would pop up immediately.
Meanwhile, as long as they don't screw me over, it's unlikely an open source alternative is going to be able to catch up to a profitable business that keeps their users happy.
It's an interesting approach, focused on incentive alignment, which is the best way to ensure quality long term.
See Mattermost for an example of a similarly positioned product that is fully open source.
If it was open source that would be more likely to happen
Sample output (should be viewable since I put `#public` at top of the document): https://bigasterisk.com/vault/esp%20cams.md
Server code: https://bigasterisk.com/code/vaulterrific/files/tip/
In-fact, I don’t really like Obsidian on Mobile, so I use iA-Writer to edit/view the Markdown files that I managed with Obsidian on the Desktop.
file (required, string) is the path to the file within the system.
What kind of path, within what system? It's not clear that the 'file' type couldn't just be another kind of 'link'. If various fields like 'background' were defined to be URLs, that would offload a lot of complexity onto existing web specs.
> cover fills the entire width and height of the node.
Does that work like the CSS background-size: cover; or background-size: 100% 100%;?
> ratio maintains the aspect ratio of the background image.
Does that mean CSS cover? contain? Something else?
Colors can be specified in hex format, e.g. "#FFFFFF". Six preset colors exist, mapped to the following numbers:
1 red
2 orange
3 yellow
4 green
5 cyan
6 purple
I'm sure everyone will infer the same color codes here.Maybe the file format isn't meant to reproduce the exact same look in different software, but merely communicate user intent. Your guess is as good as mine.
It looks really promising though! I'm definitely interested in seeing this grow.
Then, conforming implementations could render any document just by following the instructions, while editors that actually understand them can provide their own high-level control.
The trick is keeping it editable, which postscript doesn’t do well.
Example: if the language is strong enough to, say, implement force-directed node layout, an editor that doesn’t understand it could still add nodes and they would move around according to the document author’s wishes whereas perhaps the original editor might have more powerful editing capabilities.
I like to think of it like the unist ecosystem for ASTs. Unist provides a baseline spec that compatible tools can use to comb an AST. Then, specific AST tools like hast for HTML or sast for CSS/SCSS can add their own metadata on top.
I'm imagining an ecosystem of "adapters" that would help you translate some of the metadata across providers.
[1] https://twitter.com/KinopioClub/status/1768037179400331692?t...
But to be a total downer, this spec looks like an extremely rudimentary graph file format, of which there are already like a hundred and all define more visual aspects than this spec.
Also, this is not very json-ish, but optimizing your serializer so your metadata is always written first is pretty handy for embedding, since it allows you to use a pull parser and do useful things before the entire doc is parsed. (e.g. picture a huge doc being embedded and starting out as just a box and having its elements filled in async. You can't do that well if you don't know the bounding box ahead of time)
I don't think this really has much to do with the "infinite" nature of the canvas. Individual instances of infinite canvases have a finite size. :)
Summary: "node: { type: ..., x/y/color }; edge: { from/to: ..., color/label/... }"
Refreshingly simple, especially paired with their "gif of usage": https://obsidian.md/canvas
UPDATE: Figured out how to create one:
1. Install and then open Obsidian
2. Click the "Create new canvas" icon - third down of the icons on the left
3. Add some stuff to the canvas - I double clicked to create a few boxes, put some text in them and then dragged lines between them
4. In the ... menu on the top right click "Reveal in Finder"
You can then open the file it reveals in a text editor to see the JSON Canvas format.
https://github.com/obsidianmd/jsoncanvas/blob/main/sample.ca...
If I'm reading between the lines, this is only supported by Obsidian (as it's by Obsidian)? Considering the complexities and 'malleability' of infinite canvas tools, it would have been prudent to have involved or approached some of the major players in this space, like Excalidraw, Draw.io, Microsoft, Figma. Or at least started at version 0.1 and once it gained a wider consensus, release 1.0.
Besides Argdown, Markdown itself provides some built-in features that can be utilised to construct graphs. Coordinates for instance, can be stored as HTML comments or link alt texts, e.g. `[node](# ("x:25,y:50"))`. Edge, shape types and other data could similarly be stored in alt text fields as serialised JSON or in separate blocks using link reference definitions. [1]
One step further, Markdown lists could be used to store subtrees while cycles as Obsidian block links. This also allows you to encode ancestral, sibling and descendant relations:
- [root](# ("x:25,y:50"))
- leaf
- [link](#^id)
- another leaf^id
You'd then be able to interleave prose and graph structures in a single file rather than dealing with two separate parsing structures. Even better, the end result would still be Markdown compliant.[0]: https://argdown.org/
[1]: https://github.github.com/gfm/#link-reference-definition
As far as the spec, I don't really like the idea of forcing well-known types for the nodes. A generic spec should allow for entirely generic nodes that can represent themselves to consuming functions with a 'type' property as a key, as well as arbitrary data types linked to arbitrary nodes. For instance: one of my use cases is an 'addition' node, which would take two number values and produce a number value. This node would also use an entrance execution pin as well as an exit execution pin.
If the spec were to include a 'pin' data type and capture the type keys and labels for pins, those pins could be stored as a list on the node. Then, the type property could just tell the executing context how to route the node data and the pin properties would bring type safety to the functional inputs passed to the mapped function.
Anyway, I assume all of that is out of scope for initial offerings, but that's my two cents on a generalized node spec. Regardless, thanks again for the sweet, simplistic node graph implementation!
This is instead about whiteboard-style graphs. Which is useful, but I find the branding "An open file format for infinite canvas data" to be confusing. Nothing in it implies whiteboards or graphs to me. The fact that the canvas is infinite doesn't even have an obvious influence on the file format, apart from the absence of a canvas size property.
Node: only id, x, y would be necessary. This would allow for point nodes. We could even imagine letting go of x and y to signify that the position of the node is not fixed and could be recomputed in real time by the program.
Even ids could be optional, why require them if they are not referenced?
An added bonus of having point nodes is that you get freehand drawings for free: every stroke is a series of connected dots. Maybe it is an anti-feature though, depending on your vision.
not a great experience on mobile (iOS/safari) - pinch to zoom and drag to scroll are both a bit busted, the zoom ui controls can disappear altogether, and there’s no ui indication that the box on the right, that contains the details of the spec, is actually a scrollable container (ie no scroll bar visible)
The items on our infinite canvas are more akin to a desktop app, where most objects are application windows.
If you click "Toggle output" on the linked page, you can see the code for the page itself
It has basically changed the way I interact with LLMs and research and plan things
Do you plan to make the TypeScript definition part of this new site?
https://github.com/obsidianmd/obsidian-api/blob/master/canva...
For me it's easier to read TS format.
I've stayed away from their Canvas feature largely because it is.. not that. Not because the Obsidian developers have kept it locked down in some crazy proprietary format, but like a JSON file representing the canvas is pretty useless without something to interpret it and these days Obsidian is still the only implementation.
So I kind of hope this takes off. Having a second source available would make me feel a lot more comfortable trying out the canvas feature.
- I'd like to see the `file` node have a mime type field.
- Why limit links to URLs, as opposed to URIs?
- You might want to put a version field in the top level!
https://youtu.be/GblI7GI0jQ4?t=85
(Alas, the original desktop app (iMapping, in Java) as shown in the video is no longer being developed, and now they are only developing the web app (Infinitymaps) only run in the browser, which imo is not the best fit for infinite canvas apps which can be very resource-intensive)
Also see the app author's own all-purpose mega-map that he used to organize everything: https://youtu.be/bTQWL5wmdZY?si=6VrnPIErOzasisEe
I agree with you that I don't think it maps well to the mental model of the brain. Seeing the youtube video link in the sibling comment, when the user is completely zoomed out and can see everything, I just feel overwhelmed looking at it all. Maybe it's the nature of the boxes having different scales that you can't compare them as easily to each other compared to just a regular canvas where things just place in two dimensions. Each time you zoom into a canvas, the transition causes a lost sense of place and space, akin to walking into a doorway to a room and wondering why you walked there to begin with.
BUT, I imagine this being more useful for creating non-frontend tools. For instance a server that returns subsets of nodes for a particular viewport. Or something that may index nodes, or produce search results. Or tools that simply generates canvases from other data as a one-way operation.
Braintree payments did this well when they were still a startup. They had to collaborate with their direct competitors to create a standard for the entire POS induustry.
What prevents us from doing this here as well?
Are there other solutions that take JSON and do the same thing just as easily?
I've been looking for a good equivalent to QGraphicsView and HTML Canvas is not it (HTML Canvas is a raster image, QGraphicsView is a size-independent fixed-sized, scrollable canvas with indexed objects).
To take this specific example, some of it feels very similar to HTML. label, links, sections, groups, anchors, background fill options...I would have been tempted to define as a subset of HTML that is supported. Then if I wanted JSON, say how JSON maps to HTML. voila suddenly everything is standardly named and creatable. This means backgroundStyle is replaced by background-size = cover or contain. It means that those six preset colors are replaced by all HTML standard colors. Voila no one needs to learn different concepts or definitions. Try that existing standard, try 2 more and only if they don't work invent a new one. Please. I say this as someone that inherited standards invented by teams that I then had to try and train hundreds of users on. Funnily enough the previous people left when the coding was done, without teaching the users. THey probably left to implement version 2 elsewhere. :)