Generate Gantt charts from JSON descriptions using matplotlib
Project description
jsonantt
jsonantt generates beautiful Gantt chart images from a simple JSON description.
Charts are rendered with matplotlib so they can be saved as .png, .pdf, .svg, and more.
Features
- Infinitely nestable tasks — define sub-tasks, sub-sub-tasks, etc.
- Auto date computation — parent task start/end are derived automatically from children when not specified.
- Milestone markers — easy
"milestone": trueflag with chart-level defaults and per-milestone marker overrides. - Optional task descriptions and extra fields — add long-form context per task, plus arbitrary metadata like assignee or cost for table output.
- Fully colourable — set colours per-task; children inherit their parent's colour.
- Recursive child colour lightening — inherited subtask colours can optionally lighten at each nested level.
- Clean, indented y-axis labels — task names are left-aligned with proper indentation per depth level.
- Table output mode — render a task summary table with the same hierarchy and colour cues.
- Funded burn output — render cost-over-time charts and period matrix tables from numeric task fields.
- PNG / PDF / SVG output — whatever matplotlib supports.
Installation
pip install jsonantt
Or, directly from source:
git clone https://github.com/briday1/jsonantt.git
cd jsonantt
pip install -e .
Quick start
1. Create a JSON description
{
"title": "My Project",
"dateformat": "%Y-%m-%d",
"style": {
"milestone_color": "#FFD700",
"milestone_marker": "D"
},
"tasks": [
{
"name": "Phase 1 – Planning",
"children": [
{ "name": "Requirements", "start": "2024-01-08", "end": "2024-01-19" },
{ "name": "Architecture", "start": "2024-01-15", "end": "2024-01-31" }
]
},
{ "name": "Planning done", "milestone": true, "date": "2024-01-31" },
{
"name": "Phase 2 – Build",
"color": "#70AD47",
"children": [
{ "name": "Backend", "start": "2024-02-01", "end": "2024-03-01" },
{ "name": "Frontend", "start": "2024-02-12", "end": "2024-03-08" }
]
},
{ "name": "Launch", "milestone": true, "date": "2024-04-01", "color": "#FF5757" }
]
}
2. Generate the chart or table
CLI:
jsonantt project.json project.png
jsonantt project.json project.pdf # vector PDF
jsonantt project.json project.svg # scalable SVG
jsonantt --dpi 300 project.json project.png # high-resolution PNG
jsonantt -r 1 project.json project.png # top-level tasks only
jsonantt --renderdepth 2 project.json project.png # include one child level
jsonantt -t project.json project-table.png # task name / description table
jsonantt -t project.json project-table.csv # CSV table export
jsonantt agreed.json compare.png --compare actual.json # outline vs actual compare chart
jsonantt -t agreed.json compare-table.csv --compare actual.json # compare table with signed offsets
jsonantt --burn project.json burn.png --burn-field cost --burn-period month --burn-group 0
jsonantt --burn-table project.json burn-table.csv --burn-field cost --burn-period quarter --burn-group 0
jsonantt project.json project.png --date-line today --date-line-color "#C00000" # single target line
Python API:
from jsonantt import load_chart, render_burn_chart, render_burn_table, render_chart, render_compare_chart, render_compare_table, render_table
config = load_chart("project.json")
actual = load_chart("actual.json")
render_chart(config, "project.png", dpi=150)
render_chart(config, "summary.png", dpi=150, render_depth=1)
render_table(config, "project-table.png", dpi=150)
render_compare_chart(config, actual, "compare.png", dpi=150)
render_compare_table(config, actual, "compare-table.csv", dpi=150)
render_burn_chart(config, "burn.png", dpi=150, field="cost", period="month", group_by=0)
render_burn_table(config, "burn-table.csv", dpi=150, field="cost", period="quarter", group_by=0)
render_depth=0 renders all nested levels. 1 renders only top-level tasks, 2 includes one level of children, and so on.
--table / -t switches the output to a table view. By default it renders Task, Name, and Description. The Task column keeps hierarchy numbering, the Name column stays unindented, style.table_colorize controls the side color accent, and style.table_show_markers controls whether milestone rows use a diamond marker in that gutter.
You can customize the table columns with style.table_columns. Each entry can be either a string field name or an object with field and optional title. The special fields task, name, and description preserve the existing behavior; any other field is read from the task object, including extra task keys like assignee, cost, or fte.
Column objects can also opt into numeric aggregation. Use rollup: "sum" to show a recursively summed value on parent rows, total: true to add a footer total for that column, and total_level to control which task depth contributes to that footer. When rollup is enabled, totals default to depth 0 so parent rollups are not double-counted.
For display-only unit conversion, set display_factor on the column. This multiplies the numeric value one time at render/export time, after the raw value has been read and after any rollup sum has been calculated. That makes it suitable both for converting FTE into annual dollars and for showing large values in k or M units by pairing a factor with a custom header title.
{
"style": {
"table_columns": [
"task",
"name",
{ "field": "assignee", "title": "Owner" },
{ "field": "fte", "title": "Annual Cost ($)", "rollup": "sum", "total": true, "total_level": 0, "display_factor": 250000 },
"description"
]
},
"tasks": [
{
"name": "API Design",
"start": "2024-01-01",
"end": "2024-01-10",
"assignee": "Morgan",
"fte": 1.2,
"description": "Finalize endpoints and review contracts."
}
]
}
Examples:
- FTE to annualized dollars:
{ "field": "fte", "title": "Annual Cost ($)", "display_factor": 250000 } - Dollars to thousands:
{ "field": "cost", "title": "Cost (k$)", "display_factor": 0.001 } - Dollars to millions:
{ "field": "cost", "title": "Cost ($M)", "display_factor": 0.000001 }
Description and Name cells wrap to the measured rendered width of the column, and the row height expands to fit the wrapped lines. Very long unbroken tokens are not split mid-word, so they can still clip horizontally.
--milestones-only works with --table to render only milestone rows in a dedicated milestone table. --no-milestones does the opposite and excludes milestones.
If the table output path ends in .csv, jsonantt writes CSV instead of an image.
--compare turns the first JSON input into the planned/agreed baseline and overlays the second JSON as the updated/actual state. In compare charts, planned bars are drawn as slightly larger unfilled outlines, actual bars are drawn normally, removed planned tasks are struck through with no actual bar, and actual-only tasks render as normal filled bars. In compare tables and CSV output, the Offset column shows signed duration changes like +2d, -1w, +3mo, or +1y; milestones use the signed date shift instead.
--burn renders a funded burn chart from a numeric task field such as cost. Burn output always allocates from the lowest tasks that actually carry a numeric value, spreads each task uniformly over its duration, and only then groups the result. That means collapsing to top-level or total views does not lose deeper task spend.
Use --burn-period to choose day, week, month, quarter, or year. Use --burn-group total for one overall series, --burn-group leaf for the lowest direct-cost tasks, or a depth integer where 0 is top-level, 1 is subtasks, 2 is sub-subtasks, and so on. --burn-table renders the same burn data as a matrix with one row per grouped task and one column per time bucket. --burn-display-factor applies a display-only multiplier once, after burn allocation, so the same scaling rules used for cost tables also work here.
Use --date-line to draw a single vertical reference line on chart outputs. It accepts either a date in the input file's dateformat or the special value today. Use --date-line-color to control its color.
JSON reference
| Field | Type | Description |
|---|---|---|
title |
string | Optional chart title shown at the top |
dateformat |
string | strptime format string (default: "%Y-%m-%d") |
start |
date string | Optional chart x-axis start date (overrides task dates) |
end |
date string | Optional chart x-axis end date |
style |
object | Visual style overrides (see below) |
tasks |
array | Top-level list of task objects |
Task object
| Field | Type | Description |
|---|---|---|
name |
string | Required. Task label |
description |
string | Optional long-form text used by table output |
| any other key | any JSON value | Preserved on the task and available to style.table_columns for table output |
id |
string | Unique identifier used for not_before references |
start |
date string | Bar start date |
end |
date string | Bar end date |
duration |
string or int | Duration from start (or resolved not_before end): "14d", "2w", "3m", "2y", or a plain integer (days) |
not_before |
string | id of another task — this task starts immediately after that task ends |
color |
CSS hex string | Bar colour, or milestone colour override for an individual milestone (e.g. "#4472C4") |
milestone |
boolean | Render as a diamond milestone instead of a bar |
date |
date string | Milestone date (used when milestone: true) |
marker |
string | Milestone marker override for an individual milestone (matplotlib marker symbol such as "D", "o", "s", "*") |
marker_size |
number | Override milestone diamond size in points |
bold |
boolean | Render label in bold (top-level tasks are auto-bolded by default) |
children |
array | Nested sub-tasks (infinitely nestable) |
Auto date computation: When a task has
childrenbut no explicitstart/end, the dates are computed automatically as the earliest child start and latest child end, recursively.
Duration formats:
d/day/days,w/week/weeks,m/month/months,y/year/years— e.g."14d","2w","3m","1y".
Style object
| Field | Default | Description |
|---|---|---|
width |
14 |
Figure width in inches |
row_height |
0.3 |
Height of each task row in inches |
bar_height |
0.5 |
Bar height as a fraction of row_height |
font_size |
12 |
Base font size in points |
indent_size |
3 |
Spaces added per depth level in labels |
label_fraction |
0.0 |
Fraction of figure width used for labels; 0 means auto-size from measured text width |
subtask_lightening_pct |
0.0 |
Percentage to lighten inherited subtask colours at each nested level; 20 means each inherited step moves 20% toward white |
colors |
palette | Array of default hex colours cycled per top-level task |
background |
"#FFFFFF" |
Figure background colour |
grid_color |
"#E0E0E0" |
Vertical gridline colour |
row_band_color |
"#F5F5F5" |
Alternating row band colour |
milestone_color |
"#FFD700" |
Default milestone colour |
milestone_marker |
"D" |
Default milestone marker symbol |
milestone_size |
14 |
Default milestone marker size in points |
major_tick |
null |
Major tick unit: "year", "quarter", "month", "week" |
minor_tick |
null |
Minor tick unit: "quarter", "month", "week", "day" |
major_grid_width |
2.0 |
Major gridline linewidth |
minor_grid_width |
1.5 |
Minor gridline linewidth |
tick_position |
"top" |
X-axis label position: "top", "bottom", or "both" |
bold_tasks |
true |
Auto-bold top-level (depth 0) task labels |
number_tasks |
true |
Prefix task labels with hierarchy numbers like 1, 1.1, 1.2 |
table_colorize |
true |
Show a task-coloured accent bar in table output; when false, both accent bars and milestone markers are suppressed |
table_show_markers |
true |
Replace the accent bar with a milestone diamond for milestone rows in table output when table colours are enabled |
table_columns |
[] |
Ordered table column definitions. Empty means the default Task, Name, Description columns. Entries can be field-name strings or objects like { "field": "cost", "title": "Cost (k$)", "rollup": "sum", "total": true, "total_level": 0, "display_factor": 0.001 } |
Examples
See the examples/ folder for ready-to-run JSON files.
Simple project
examples/simple.json — a five-phase project with milestones
Dependencies
examples/dependencies.json — id, duration, and not_before
Render depth
examples/renderdepth.json — nested tasks that are useful with -r / --renderdepth
Full depth, render_depth=0:
One child level, -r 2:
Top level only, -r 1:
Table output, -t:
Milestones only, -t --milestones-only:
No milestones, -t --no-milestones:
Try the same file with different depth limits:
jsonantt examples/renderdepth.json examples/renderdepth-all.png # full depth
jsonantt -r 1 examples/renderdepth.json examples/renderdepth-top.png # top-level only
jsonantt -r 2 examples/renderdepth.json examples/renderdepth-mid.png # one child level
jsonantt -t examples/renderdepth.json examples/renderdepth-table.png # task table view
jsonantt -t --milestones-only examples/renderdepth.json examples/renderdepth-milestones.png # milestones only
jsonantt -t --no-milestones examples/renderdepth.json examples/renderdepth-no-milestones.png # exclude milestones
jsonantt -t examples/renderdepth.json examples/renderdepth-table.csv # CSV table export
Funding burn
examples/funding.json — direct task costs allocated uniformly over time, then grouped into top-level burn output
Monthly burn chart, --burn --burn-period month --burn-group 0:
Quarter burn table, --burn-table --burn-period quarter --burn-group 0:
Color schemes
examples/colors.json — custom palette, background, grid, row band, milestone colours, and custom milestone markers
Compare mode
examples/compare-planned.json and examples/compare-actual.json — planned vs actual comparison with a CLI date line
Compare chart:
Compare table image:
CSV export:
Description wrapping
examples/description-wrap.json — demonstrates normal word wrapping in table output and the edge case where a single unbroken token will not wrap
Table output, -t:
CSV export:
examples/description-wrap-table.csv
Custom table fields
examples/costs.json — shows style.table_columns with custom Owner and Cost columns sourced from task fields like assignee and cost
CSV export:
Complex roadmap
examples/complex.json — a multi-year roadmap with deep nesting, task descriptions, and custom colours
Table output, -t:
Milestones only, -t --milestones-only:
No milestones, -t --no-milestones:
How to Run
jsonantt examples/simple.json examples/simple.png # basic milestones
jsonantt examples/dependencies.json examples/dependencies.png # not_before scheduling
jsonantt examples/renderdepth.json examples/renderdepth.png # full nested view
jsonantt -r 1 examples/renderdepth.json examples/renderdepth-top.png # top-level only
jsonantt -r 2 examples/renderdepth.json examples/renderdepth-mid.png # one child level
jsonantt -t examples/renderdepth.json examples/renderdepth-table.png # task table with descriptions
jsonantt -t --milestones-only examples/renderdepth.json examples/renderdepth-milestones.png # milestone table
jsonantt -t --no-milestones examples/renderdepth.json examples/renderdepth-no-milestones.png # table without milestones
jsonantt -t examples/renderdepth.json examples/renderdepth-table.csv # CSV export
jsonantt examples/colors.json examples/colors.png # custom palette and background
jsonantt examples/compare-planned.json examples/compare.png --compare examples/compare-actual.json --date-line 2024-03-01 --date-line-color "#C00000" # compare chart with target line
jsonantt -t examples/compare-planned.json examples/compare-table.png --compare examples/compare-actual.json # compare table image
jsonantt -t examples/compare-planned.json examples/compare-table.csv --compare examples/compare-actual.json # compare table CSV
jsonantt -t examples/description-wrap.json examples/description-wrap-table.png # wrapped descriptions table image
jsonantt -t examples/description-wrap.json examples/description-wrap-table.csv # wrapped descriptions CSV
jsonantt -t examples/costs.json examples/costs-table.csv # custom cost/owner columns CSV
jsonantt examples/complex.json examples/complex.png # deep roadmap example
jsonantt -t examples/complex.json examples/complex-table.png # full complex table
jsonantt -t --milestones-only examples/complex.json examples/complex-milestones.png # complex milestones only
jsonantt -t --no-milestones examples/complex.json examples/complex-no-milestones.png # complex without milestones
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jsonantt-2026.14.tar.gz.
File metadata
- Download URL: jsonantt-2026.14.tar.gz
- Upload date:
- Size: 45.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d947184f7bee7036ef27c6a738e334c00c1c4f78019e21278d5d113a57b049b0
|
|
| MD5 |
93706bc685be161b1df55d1fc92e5a52
|
|
| BLAKE2b-256 |
b115593bcd7e436139ba8667accf46dfa1d39c225dc2910c6f68090c4183007b
|
File details
Details for the file jsonantt-2026.14-py3-none-any.whl.
File metadata
- Download URL: jsonantt-2026.14-py3-none-any.whl
- Upload date:
- Size: 35.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f02798dc048dd61e0e6f02e891765653a5404257e8cda488cf968703a6cf5c7
|
|
| MD5 |
b10b15c78db1eb003e7eebbbf2e1535d
|
|
| BLAKE2b-256 |
fabfdc8b6be9f32df444dcd661134ed0d7e72bc71e57fb6e77b63b0a56aed5cf
|