JFDG Specification v1.0
JsonFiddle Diagram Graph — a JSON-native format for node-edge diagrams.
Overview
JFDG is an open diagram format that uses plain JSON to describe nodes, edges, and visual properties. Unlike text-based formats (Mermaid, DOT) or XML-heavy formats (GraphML), JFDG is designed to be:
- Human-readable — write and edit diagrams in any text editor
- JSON-native — no compilation step, no custom syntax to learn
- IDE-friendly — full autocomplete and validation via JSON Schema
- Metadata-rich — attach arbitrary key-value data to any node
- Visually complete — positions, icons, themes, and grouping are part of the format
File Extension & MIME Type
| Property | Value |
|---|---|
| Extension | .jfdg |
| MIME type | application/vnd.jsonfiddle.jfdg+json |
| Character encoding | UTF-8 |
| Schema URL | https://jsonfiddle.com/schemas/jfdg-1.0.json |
Quick Example
{
"$schema": "https://jsonfiddle.com/schemas/jfdg-1.0.json",
"nodes": [
{ "id": "api", "label": "API Gateway", "iconUrl": "mdi:shield-check", "position": { "x": 0, "y": 100 } },
{ "id": "auth", "label": "Auth Service", "iconUrl": "mdi:lock", "position": { "x": 300, "y": 0 } },
{ "id": "db", "label": "PostgreSQL", "iconUrl": "mdi:database", "position": { "x": 300, "y": 200 } }
],
"edges": [
{ "from": "api", "to": "auth", "label": "verify token" },
{ "from": "api", "to": "db", "label": "query" }
]
}
Document Structure
A JFDG document is a JSON object with three top-level keys:
{
"nodes": [...], // Required — list of nodes
"edges": [...], // Required — list of edges
"configuration": {...} // Optional — layout and theme settings
}
Nodes
Each node is an object with a required id and optional visual/structural properties.
Required Properties
| Property | Type | Description |
|---|---|---|
id | string | Unique identifier. Must match ^[a-zA-Z][a-zA-Z0-9_-]*$ |
Optional Properties
| Property | Type | Description |
|---|---|---|
label | string | Display label. Defaults to id if omitted |
position | { x, y } | Canvas position in pixels. Omit all positions for auto-layout |
iconUrl | string | Iconify identifier (e.g., mdi:database) or image URL |
imageUrl | string | URL to an image displayed on the node |
width | number | Explicit width in pixels (groups) |
height | number | Explicit height in pixels (groups) |
nodes | node[] | Child nodes — makes this a group node |
custom_theme | theme | Per-node color override |
Metadata (Additional Properties)
Any property not in the reserved list above is treated as metadata and displayed in the node's detail panel. This is a core JFDG feature — it allows diagrams to carry structured data.
{
"id": "users-table",
"label": "Users",
"iconUrl": "mdi:database",
"email": "string",
"name": "string",
"created_at": "timestamp",
"role": "enum(admin, user, guest)"
}
The metadata fields (email, name, created_at, role) are displayed inside the node card, making JFDG useful for ER diagrams, API documentation, infrastructure maps, and any diagram where nodes carry data.
Group Nodes
A node with a nodes array becomes a group — a visual container for child nodes.
{
"id": "vpc",
"label": "VPC",
"position": { "x": 100, "y": 50 },
"width": 500,
"height": 300,
"nodes": [
{ "id": "web", "label": "Web Server", "position": { "x": 30, "y": 40 } },
{ "id": "db", "label": "Database", "position": { "x": 250, "y": 40 } }
]
}
Child node positions are stored in absolute canvas coordinates, the same as top-level nodes. A renderer may convert them to parent-relative coordinates internally, but the source JSON should keep absolute positions so moving nodes between groups does not require coordinate translation.
Groups can be nested by placing a group node inside another group's nodes array:
{
"id": "vpc",
"label": "VPC",
"position": { "x": 100, "y": 50 },
"width": 600,
"height": 500,
"nodes": [
{
"id": "workflow",
"label": "Workflow",
"position": { "x": 160, "y": 180 },
"width": 360,
"height": 240,
"nodes": [
{ "id": "validate", "label": "Validate Request", "position": { "x": 220, "y": 230 } },
{ "id": "fetch", "label": "Fetch", "position": { "x": 240, "y": 330 } }
]
}
]
}
Groups render as dashed-border containers with a floating label. Node IDs must remain globally unique across all nesting levels.
Nested group bounds should fit inside their parent group. If a nested group is larger than, or positioned outside, its parent, renderers may treat it as a top-level visual group to keep both groups selectable on the canvas.
Edges
Each edge connects two nodes by their id.
| Property | Type | Required | Description |
|---|---|---|---|
from | string | Yes | Source node id |
to | string | Yes | Target node id |
label | string | No | Text displayed on the edge |
style | string | No | "solid" (default), "dashed", or "dotted" |
direction | string | No | "forward" (default), "backward", "both", or "none" |
animated | boolean | No | When true, dashes march along the edge to indicate active flow |
bidiEffect | string | No | Bidirectional animation effect: "glow" (default), "arrows", "pulse", or "duplex" |
waypoints | { x, y }[] | No | Manual control points in canvas coordinates |
Direction
Controls where arrowheads appear:
| Value | Visual | Description |
|---|---|---|
"forward" | A --> B | Arrow at target (default) |
"backward" | A <-- B | Arrow at source |
"both" | A <--> B | Arrows at both ends |
"none" | A --- B | No arrows |
Animation
When animated is true, dashes march along the edge indicating active flow. The animation always moves from source to target regardless of the direction setting — arrowheads communicate directionality, animation communicates liveness.
{ "from": "api", "to": "db", "label": "queries", "style": "dashed", "direction": "both", "animated": true }
Bidirectional animated edges can choose a bidiEffect:
| Value | Description |
|---|---|
"glow" | A single glowing packet travels out and back |
"arrows" | A compact traveling marker emphasizes direction changes |
"pulse" | Two pulses repeatedly move from the center toward both ends |
"duplex" | Two counter-moving stream lanes show simultaneous traffic |
Edges can connect nodes across group boundaries. Handle selection (which side the edge connects from/to) is computed automatically based on relative node positions.
Manual Waypoints
By default, edges use generated routing. Select an edge in the editor to reveal two draggable waypoints; moving either point stores a two-point waypoints route for that edge:
{
"from": "client",
"to": "gateway",
"label": "HTTPS",
"waypoints": [
{ "x": 180, "y": 80 },
{ "x": 360, "y": 80 }
]
}
Removing waypoints returns the edge to generated routing. Older files with more than two waypoints still load, but the editor presents them as two draggable control points.
Configuration
Optional global settings.
{
"configuration": {
"layout": "auto",
"theme": "azure",
"schema": {
"users-table": {
"email": "string",
"role": "enum"
}
}
}
}
| Property | Type | Description |
|---|---|---|
layout | "auto" | "manual" | auto uses ELK algorithm. manual uses node positions. If any node has a position, defaults to manual |
theme | string | Named preset: mono, sand, azure, midnight, forest, etc. |
schema | object | Type hints for metadata fields, keyed by node id then field name |
Layout Modes
Auto Layout
When layout is "auto" (or when no nodes have position values), the ELK algorithm arranges nodes automatically. This is ideal for quickly visualizing data without manually positioning.
Manual Layout
When any node has explicit position coordinates, the layout defaults to manual. Positions are preserved exactly as specified, and users can drag nodes on the canvas.
Theming
JFDG supports both global themes (via configuration.theme) and per-node overrides (via custom_theme).
Theme Object Structure
{
"NODE": { "NODE": "#f0f7ff", "HEADER": "#dbeafe" },
"EDGE": { "EDGE": "#60a5fa", "LABEL": "#3b82f6" },
"TEXT": { "TEXT": "#1e3a5f", "ROW_KEY": "#2563eb" }
}
Built-in Presets
| ID | Name | Mode |
|---|---|---|
mono | Mono | Light |
sand | Sand | Light |
azure | Azure | Light |
midnight | Midnight | Dark |
forest | Forest | Dark |
Format Comparison
| Feature | JFDG | Mermaid | DOT (GraphViz) | GraphML |
|---|---|---|---|---|
| Syntax | JSON | Custom DSL | Custom DSL | XML |
| Human-readable | Yes | Yes | Moderate | No |
| IDE autocomplete | Yes (JSON Schema) | Limited | Limited | Limited |
| Metadata on nodes | Yes (arbitrary KV) | No | Limited attributes | Yes (verbose) |
| Visual positions | Native | No | Limited | Yes |
| Grouping | Native nesting | Subgraphs | Subgraphs | Hyperedges |
| Theming | Built-in | CSS | Attributes | External |
| File size | Small | Smallest | Small | Large |
Icons
JFDG uses Iconify notation for icons, giving access to 200,000+ icons from 150+ icon sets:
- Material Design:
mdi:database,mdi:server,mdi:lock - Simple Icons (logos):
simple-icons:aws,simple-icons:docker - Logos:
logos:react,logos:kubernetes
Any valid Iconify identifier works. Alternatively, iconUrl can be a direct URL to an image.
Validation
A JFDG document is valid if:
- It is valid JSON
- It has a
nodesarray and anedgesarray at the top level - Every node has a unique
idmatching^[a-zA-Z][a-zA-Z0-9_-]*$ - Every edge references existing node ids in
fromandto - No circular group nesting (a node cannot be its own ancestor)
Versioning
The schema URL includes the version number: jfdg-1.0.json. Future versions will maintain backward compatibility — new fields will be additive, existing fields will not change semantics.