Unify declarative Python diagrams, technical documentation, vector PDFs, and 16:9 presentations inside a single version-controlled Git repository.
Drawlib Official Showcase Deck
Pure Python (>=3.11) • Apache 2.0 License
A declarative, pure-Python graphics and documentation engine that treats architectural diagrams, quantitative charts, animations, and technical documents as first-class software artifacts.
>=3.11) using variables, loops, type-checked models, and reusable project helpers (styles.py & utils.py).matplotlib patch boilerplate.site), Linear Technical Specs & Vector PDFs (doc), 16:9 Widescreen Slide Decks (slide), and Standalone Images (image) from one unified CLI.drawlib rules show) and headless coordinate-grid preview (drawlib show -g), enabling AI coding agents to write, inspect, and self-heal visual diagrams autonomously.A comprehensive 7-chapter tour from the philosophy of Illustration as Code to high-level diagrams, animations, and AI workflows.
This 60-slide presentation is 100% authored and compiled with Drawlib:
drawlib Python blocks in docs/slide_about_drawlib_src/styles.py and reusable layout helpers in utils.py<svg> with automatic @font-face bundling<canvas> Animations: Step-by-step animated diagrams controlled via A or Presenter View (P)Why traditional technical documentation inevitably drifts out of sync with reality.
src/)
.png screenshots and paste them into external pages.Why black-box heuristic layout DSLs hit a wall on real-world production architectures.
Text-to-diagram DSLs (Mermaid, PlantUML, DOT) rely on rigid, opaque layout heuristics:
X=80" or "route this error path around the VPC border".A ~~~ B) and hidden links just to nudge boxes.for loops, math expressions, conditional branches, or shared Python functions.Why general-purpose plotting libraries and raw vector XML are too low-level for architecture diagrams.
When engineers outgrow text DSLs, they sometimes reach for raw <svg> templates or Python's matplotlib.patches:
matplotlib requires 40–60 lines of FancyBboxPatch, ConnectionPatch, Path, and manual bounding-box math.<text dy="..."> baseline offsets and no automatic word/boundary clipping.How Drawlib bridges the gap between GUI canvases, text DSLs, and low-level plotting libraries.
Write clean, type-checked Python on the left—get publication-grade vector diagrams on the right.
from drawlib.diagrams.architecture import (
ArchitectureDiagram, GcpIcon, Node, NodeGroup, PhosphorIcon,
)
from drawlib.styles import Styles
d = ArchitectureDiagram(
node_style=Styles.Primary,
node_text_style=Styles.DarkBold,
edge_style=Styles.DarkBold,
edge_text_style=Styles.Dark,
node_card_style=Styles.Neutral,
)
vpc = d.add(NodeGroup("Production Cloud VPC", padding=7), (26, 8))
client = d.add(Node("Web Client", width=18, height=16, icon=PhosphorIcon.GLOBE), (8, 42))
gw = vpc.add(
Node("API Gateway", width=20, height=16, icon=GcpIcon.CLOUD_API_GATEWAY,
card_style=Styles.PrimaryFlat, text_style=Styles.WhiteBold),
(16, 34),
)
sql = vpc.add(Node("Cloud SQL", width=20, height=16, icon=GcpIcon.CLOUD_SQL), (54, 50))
bq = vpc.add(Node("BigQuery", width=20, height=16, icon=GcpIcon.BIGQUERY), (54, 18))
d.connect(client, gw, label="HTTPS")
gw.fork([sql, bq], at_x=58, padding=1.5)
d.draw()
NodeGroup wraps child nodes automatically..connect() and .fork() clip cleanly at node boundaries.Code, diagrams, and prose evolve together in atomic Pull Requests and compile deterministically.
src/, they update the drawlib diagram block in docs_src/ in the exact same commit..png blobs.drawlib check), and verifies zero syntax or type errors.drawlib build) compiles .md + .py sources into HTML documentation sites, GitHub-flavored Markdown, 1920×1080 slides, and vector PDFs..drawlib/cache.db in < 1ms, making incremental rebuilds instantaneous.Why pure Python + multimodal visual inspection unlocks autonomous documentation engineering.
drawlib rules)
uv run drawlib rules show <topic> on demand to inspect exact function signatures and style guidelines without bloating context windows.drawlib Python code in *_src/.-g (drawlib show ... -g -o preview.png).drawlib.canvas)An intuitive, resolution-independent 2D mathematical coordinate space with (0, 0) at the bottom-left.
from drawlib.canvas import clear, save, setup
from drawlib.shapes import rectangle
from drawlib.styles import Styles
clear()
setup(width=100, height=70)
rectangle((50, 35), width=40, height=20,
style=Styles.PrimaryFlat,
text="Centered at (50, 35)",
text_style=Styles.WhiteBold)
save("diagram.svg")
(0, 0): X increases rightward (0 → width); Y increases upward (0 → height).(cx, cy): Shapes (rectangle, circle, cylinder, rhombus, etc.) and icons are anchored at their geometric center (cx, cy), making horizontal (cy equal) and vertical (cx equal) alignment trivial.grid=True / -g)Inspect exact coordinates and verify margins without touching production code.
CLI Flag (-g / --grid) — Recommended
# Preview a standalone script with grid
uv run drawlib show arch.py -g -o preview.png
# Preview an embedded Markdown diagram with grid
uv run drawlib show docs_src/index.md arch.png -g -o preview.png
Programmatic setup(..., grid=True)
setup() during interactive layout design:setup(width=100, height=65, grid=True, grid_xpitch=10, grid_ypitch=10)
Client sits at (25, 30), Gateway at (52, 30), and DB at (78, 30).(x, y) coordinates directly from the rendered image.drawlib.shapes)A rich vocabulary of vector shapes—all supporting style (including rotation style.angle), text, and text_style.
arrow, arrow_l, arrow_u, arrow_arc)2D filled planar block arrows and speech callouts for prominent phase transitions and annotations.
from drawlib.shapes import (
arrow, arrow_arc, arrow_l, arrow_polyline, arrow_u, bubblespeech,
)
from drawlib.styles import Styles
# 1. Straight block arrow (supports embedded shaft text)
arrow((10, 66), (48, 66), tail_width=4, head_width=9,
head_length=6, style=Styles.PrimaryFlat,
text="Data Transfer", text_style=Styles.WhiteBold)
# 2. L-shaped & U-turn block arrows (with rounded elbow r)
arrow_l((25, 42), width=26, height=18, tail_width=3.5,
head_width=8, head_length=6, style=Styles.SecondaryNeutral.patch(shape_r=4))
arrow_u((68, 42), width=24, height=20, tail_width=3.5,
head_width=8, head_length=6, style=Styles.BlueNeutral.patch(shape_r=4))
# 3. Circular arc & multi-segment polyline block arrows
arrow_arc((25, 16), width=22, height=18, angle_start=180,
angle_end=20, tail_width=3, head_width=7, style=Styles.PrimaryNeutral)
arrow_polyline([(52, 10), (66, 10), (66, 22), (88, 22)],
tail_width=3, head_width=7, head_length=5, style=Styles.Neutral.patch(shape_r=3))
arrow() supports embedded text; for arrow_l, arrow_u, arrow_arc, and arrow_polyline, place external text() labels alongside the shaft.drawlib.lines)Precision 1D vector connectors with straight, orthogonal, filleted, arc, and polynomial Bezier routing.
from drawlib.lines import (
line, line_arc, line_bezier1, line_bezier2,
line_curved, lines, lines_curved,
)
from drawlib.styles import Styles
# 1. Straight line & orthogonal polyline
line((10, 70), (45, 70), arrow_head="->", style=Styles.DarkBold)
lines([(55, 65), (72, 65), (72, 75), (90, 75)],
arrow_head="->", style=Styles.PrimaryBold)
# 2. Curved arc spline (bend) & filleted polyline (r)
line_curved((10, 44), (45, 44), bend=0.35,
arrow_head="<->", style=Styles.PrimaryBold)
lines_curved([(55, 38), (72, 38), (72, 52), (90, 52)],
r=4.0, arrow_head="->", style=Styles.DarkBold)
# 3. Quadratic/Cubic Beziers & Elliptical Arcs
line_bezier1((10, 16), (45, 16), cp=(27, 30),
arrow_head="->", style=Styles.SecondaryBold)
line_bezier2((55, 10), (90, 24), cp1=(72, 10), cp2=(72, 24),
arrow_head="->", style=Styles.BlueBold)
"" (none), "->" (forward), "<-" (reverse), "<->" (bidirectional).style.patch(line_arrow_head_fill=True, line_arrow_head_scale=22).drawlib.text, drawlib.fonts)Universal multilingual fonts, explicit anchor alignments, and automatic @font-face SVG bundling.
from drawlib.fonts import Font, FontFile, FontMonoSpace, FontRoboto, FontSourceCode
from drawlib.styles import Styles
from drawlib.text import text, text_vertical
# Anchor alignment via halign & valign
text((20, 60), "Left Anchor",
style=Styles.DarkBold.patch(halign="left"))
text((80, 60), "Right Anchor",
style=Styles.DarkBold.patch(halign="right"))
# Built-in font families & custom TTF/OTF files
text((50, 40), "Roboto Bold",
style=Styles.PrimaryBold.patch(text_font=FontRoboto.ROBOTO_BOLD))
text((50, 25), "Custom FontFile(.ttf)",
style=Styles.AccentBold.patch(text_font=FontFile("brand.ttf")))
# Vertically stacked characters
text_vertical((12, 30), "TIER", style=Styles.MutedBold)
@font-face Bundling.svg diagrams, Drawlib automatically copies required .ttf/.otf font files into _assets/fonts/ and injects @font-face rules so browsers and PDF exports render identically anywhere.drawlib.icons, drawlib.images)1,500+ Phosphor vector glyphs, 250+ official GCP cloud icons, and in-memory Dimage transformations.
phosphor & gcp)from drawlib.icons import gcp, phosphor
from drawlib.images import Dimage, image
from drawlib.styles import Styles
# Vector Phosphor icons (5 weights: thin, light, regular, bold, fill)
phosphor.cpu((20, 65), width=12, style=Styles.PrimaryBold)
phosphor.database((45, 65), width=12, style=Styles.PrimaryFlat)
# Official Google Cloud architecture icons (multi-color PNGs)
gcp.compute_engine((20, 40), width=14, style=Styles.Neutral)
gcp.cloud_sql((45, 40), width=14, style=Styles.Neutral)
Dimage.png/.jpg/.webp assets or capture sub-diagrams in memory via get_dimage_from_code()..grayscale(), .sepia(), .invert(), .brightness(), .blur(), .mosaic(), .trim(), and .make_transparent().image((30, 15), width=16, image="_assets/linux.png")
image((65, 15), width=16, image=Dimage("_assets/linux.png").grayscale())
drawlib.styles)How systematic PascalCase tokens and neutral grounding prevent "rainbow chaos" in technical diagrams.
Styles.<Color><Variant> Matrixfrom drawlib.styles import Colors, Styles, get_intermediate_colors
# Always import PascalCase Styles & Colors!
hero = Styles.PrimaryFlat # Saturated focal point
card = Styles.PrimaryNeutral # Soft tinted-neutral card
base = Styles.Neutral # Calm slate/white baseline
zone = Styles.MutedDashed # Container boundary
# Immutable overrides via .patch()
custom = Styles.PrimaryBold.patch(line_width=3.0, text_size=12)
# Smooth color interpolation for gradients & heatmaps
ramp = get_intermediate_colors(Colors.Primary, Colors.Secondary,
num=3, include_ends=True)
PrimaryFlat, WarningFlat, DangerFlat, PurpleFlat).Styles.Neutral, Styles.PrimaryNeutral, Styles.SecondaryNeutral, Styles.BlueNeutral for standard components, reserving Styles.PrimaryFlat / Styles.AccentFlat (text_style=Styles.WhiteBold) strictly for 1–2 focal hero nodes.drawlib.smartarts)Instead of manually calculating polygon vertices and text offsets, drawlib.smartarts automates layout geometry for 10 essential diagram patterns:
| Anchor Convention | SmartArt Components | Flow Direction |
|---|---|---|
Top-Left (x, y) |
Table, TreeNode, BulletPoints, SourceCode |
Rightward (+x) & Downward (-y) |
Bottom-Left (x, y) |
ChevronProcess, GridLayout, Pyramid |
Rightward (+x) & Upward (+y) |
Center (x, y) |
MindMapNode, Cycle (default) |
Radial outward from center |
Directional (x, y) |
BoxList ("left", "right", "bottom", "top") |
Along chosen axis |
style and text_style tokens.add(..., show=True)): Returns a mutable item reference.item.show = False or patch item.style across animation frames without shifting layout coordinates.draw(xy, scale=1.0)): Uniformly scales shapes, strokes, and fonts around xy.ChevronProcess & Cycle)ChevronProcess — Phased Pipelinescorner_angle=60.0) and uniform item widths across total width.flat_left_end=True renders the first stage as a flat-backed pentagon while subsequent stages interlock seamlessly.text heading and a secondary description subtitle with independent style overrides.Cycle — Circular & Radial WorkflowsN nodes ("circle", "rectangle", or "none") evenly around orbit radius starting from start_angle=90.0 (12 o'clock).arrow_type="arc") and configurable arrow_color_mode ("monochrome", "match_source", "match_target").cycle.set_center(...) places a central hub node inside the orbit for radial governance or SRE command loops.BoxList, GridLayout, Pyramid)BoxList — Directional Card Sequences"left", "right", "bottom", or "top" orientations.GridLayout — Multi-Span Architecture Matrices(col=0, row=0) starts at the bottom-left cell; rows increment upward (+y) and columns increment rightward (+x).position=(col, row), width=w, height=h) to model full-width ingress/platform layers alongside multi-column microservice cells.outer_style (including shape_r) wraps the entire matrix in a dashed or solid boundary.Pyramid — Proportional Tiered Hierarchiesdirection="bottom" (upright) or "top" (inverted funnel) and order="vertex_to_base" or "base_to_vertex".TreeNode & MindMapNode)TreeNode — Directory & Package Hierarchies├ and └ connector lines.text_style, line_style, and margins once on the root TreeNode; all descendants inherit them automatically.TreeNode.register_drawing_item(...) and .set_drawing_item(name) to prepend or append Phosphor icons (phosphor.folder, phosphor.file_py, phosphor.file_code).MindMapNode — Two-Pass Radial & Org Treesbranch="left", "right", "top", "bottom")."ellipse", "rectangle", and borderless "none" leaf labels.Table & SourceCode)Table — Fine-Grained Matrix Stylingdraw(xy, width, height, data) distributes columns evenly, while draw_flexible(xy, column_widths, row_heights, data) gives exact per-column and per-row dimensions.set_style_cell_evenodd(even_color, even_text_style, odd_color, odd_text_style) applies alternating row stripes.set_style_cell(background_color, text_style, rows=[...], columns=[...]) highlights specific SLA breaches or target cells.set_style_border(top=..., top2=..., bottom=..., between_rows=...) controls perimeter and inner rules independently.SourceCode — Vector Code Containersshow_linenum=True)."dark", "monokai", "default", "google", "monochrome") and CJK-aware monospace fonts (font_lang="en" | "ja").drawlib.charts)Traditional workflows force you to plot charts in Matplotlib/Excel, export PNGs, and paste them next to architecture diagrams—resulting in mismatched fonts, blurry raster scaling, and broken visual alignment.
drawlib.charts(x, y) coordinate. You can draw connector arrows directly from an architecture microservice node to a live telemetry BarChart or PieChart.width, height, and radius parameters guarantee predictable margins without layout drift.Axis(scale="linear" | "log") automatically computes human-readable tick steps ($1, 2, 5 \times 10^k$) or base-10 powers ($10^k$) with custom formatters (format="{:.1f}M", unit="ms").show & draw_ratio): Toggle series.show = False without rescaling axes, or animate draw_ratio (0.0 -> 1.0) along "bottom_to_top" or "left_to_right".BarChart, LineChart, AreaChart)BarChart — Grouped, Stacked & Log Scalesorientation="vertical" | "horizontal" and bar_mode="group" | "stack".bar_r=0.8), direct value labels (value_format="{:.0f}M"), and linear or logarithmic (scale="log") axes.LineChart — Linear Polylines & Smooth Splinessmooth=True for cubic-like spline interpolation or smooth=False for exact vertex segments.line_style ("solid", "dashed", "dotted"), line_width, and point_shape ("circle", "square", "none").AreaChart — Cumulative & Overlapping Streamsmode="stack" layers series cumulatively to show total volume breakdown; mode="overlap" renders semi-transparent overlapping polygons (fill_alpha=0.35).configure_x_axis(...), configure_y_axis(...), and standalone draw_legend(xy, ...).PieChart, RadarChart, ScatterChart)PieChart — Solid Pies & KPI Donutshole_ratio=0.5 (up to 0.9) with center_text to render executive KPI donut rings.explode=2.5) and automatic label suppression on tiny wedges ($<4%$).RadarChart — Multi-Axis Trade-Off Polygonsgrid_shape="polygon" | "circle").ScatterChart — XY Plots & 3D Bubbles(x, y) coordinates or 3-tuple (x, y, radius) bubbles across dual independent linear or log axes.add_series(...) clusters with individually labeled benchmark points via chart.add(xy, label=...).GanttChart)Maintaining engineering roadmaps in external spreadsheet or GUI tools makes version control diffs impossible. GanttChart models schedules directly in Python:
add_section(name): Full-width horizontal divider bands that group tasks by workstream or team.add_task(name, start, end, style, progress=...):
start and end accept column names ("Apr") or continuous floating-point column indices (1.5 = middle of 2nd column).progress (0.0 to 1.0) renders a two-tone completion fill bar with percentage text (progress_text_style).add_milestone(name, at, style): Renders a diamond milestone marker at exact timeline coordinate at.add_dependency(from_task, to_task): Routes orthogonal finish-to-start dependency arrows between tasks.add_marker(at, style, label="Today"): Draws a vertical status line across all rows.drawlib.diagrams)Unlike black-box text-to-diagram tools (PlantUML, Mermaid) that scramble your layout whenever a label changes, drawlib.diagrams combines deterministic coordinate placement with intelligent boundary & wire automation:
xy=(x, y) to d.add(...) anchors the exact geometric center of the card or icon—ensuring aligned nodes always connect with perfectly straight horizontal or vertical wires.Connectable Protocol: Nodes, groups, entities, classes, states, and Junction waypoints share dynamic boundary clipping, start_side/end_side port selection, and smart "orthogonal" / "direct" / curved bend routing.show=True/False: Hiding any node (node.show = False) automatically hides its connected edges and dangling fork() stems while keeping NodeGroup bounds, swimlanes, and lifeline spacing 100% locked.draw(xy, scale=1.0): Scales the entire diagram proportionally around bottom-left xy.ArchitectureDiagram Fundamentals: Icons, Groups & ForksArchitectureDiagram Wires Never KinkIn naive diagramming tools, adding a 2-line label underneath an icon shifts the node's bounding center downward, causing horizontal arrows between aligned icons to develop ugly Z-bends.
xy = Card Center):
d.add(Node("Label", width=w, height=h, icon=...), xy=(x, y)), (x, y) is strictly the center of the node card (w, h).y always connect with a razor-straight horizontal line!NodeGroup Containers:
group = d.add(NodeGroup(title="...", padding=6.0), xy=(gx, gy)) creates a relative coordinate group that automatically expands its boundary box to enclose all child nodes and nested subgroups.node.fork()):
lb.fork([svc1, svc2, svc3], at_x=55.0, padding=2.0) creates a clean vertical distribution bus at at_x with orthogonal taps into every target node.GcpIcon & PhosphorIcon)drawlib.diagrams.architecture integrates directly with Drawlib's icon ecosystem:
GcpIcon (259 Official Icons):
GcpIcon.GOOGLE_KUBERNETES_ENGINE, GcpIcon.CLOUD_RUN, GcpIcon.COMPUTE_ENGINEGcpIcon.CLOUD_LOAD_BALANCING, GcpIcon.CLOUD_ARMOR, GcpIcon.CLOUD_CDNGcpIcon.CLOUD_SQL, GcpIcon.CLOUD_SPANNER, GcpIcon.CLOUD_STORAGE, GcpIcon.BIGQUERYPhosphorIcon (1,531 Vector Icons):
NodeGroupsPublic Subnet and Private Subnet groups inside an outer VPC Network group (vpc.add(NodeGroup(...), xy=...)).NodeGroup are local to that group's origin, making subnet refactoring effortless.FlowDiagram)FlowDiagram models operational workflows, CI/CD release gates, and approval processes across organizational or system swimlanes:
Start / End: Rounded stadium/pill terminals (shape_r=5.0).Process: Rectangular execution step (24 x 12).Decision: Diamond conditional gate (22 x 14) with explicit start_side="right" | "bottom" | "left" branch routing.Data: Slanted parallelogram for I/O artifacts or payloads (24 x 12).flow.add_lane(name, width=...) for vertical columns or set lane_orientation="horizontal" for horizontal rows.(x, y) canvas space—so placing steps across different lanes at the same y coordinate guarantees horizontal alignment!SequenceDiagram)SequenceDiagram turns protocol specifications into clean lifeline diagrams without manual Y-coordinate bookkeeping:
d.add(Participant("Name", width=18, height=13, icon=GcpIcon.CLOUD_RUN))vpc = d.add(ParticipantGroup(title="Zero-Trust Mesh", style=Styles.MutedDashed))autonumber=True):
a.request(b, "label"): Solid synchronous call arrow (―▶).b.reply(a, "label"): Dashed return arrow (---▶).is_async=True: Open stick arrowhead (―>) for Pub/Sub or async events.p.activate() / p.deactivate() for lifeline execution bars.p.note("...", pos="right") or d.note("...", over=[p1, p2]) for sticky notes.with Condition Frames:
with d.alt("Token Valid"): ... with d.else_("Expired"): ...with d.loop("Retry"): ..., with d.opt(...):, with d.par(...):ERDiagram)ERDiagram visualizes physical and logical relational database schemas with strict Crow's Foot cardinality markers and row-aligned foreign key connectors:
Entity(name="users", width=28.0) automatically scales its vertical height based on registered columns.entity.add_column(name, type="...", pk=True, fk=False, nullable=False) renders [PK], [FK], and mandatory field indicators cleanly aligned in the right column."1:*" (One-to-Zero-or-More || ── o<)"1:1..*" (One-to-One-or-More || ── |<)"1:1", "1:0..1", "0..1:1", "0..1:*", "*:*"start_column="id", end_column="user_id" attaches the relationship wire directly to the vertical Y-center of those specific table rows!ClassDiagram)ClassDiagram models object-oriented domain architectures, design patterns, and SDK type hierarchies in standard UML 2.0 notation:
ClassNode Structurestereotype="interface" («interface») and is_abstract=True («abstract»).add_attribute): Automatic visibility prefixes (+ public, - private) and static underlines.add_method): Parameter signatures, return types, and abstract method styling.relationship_type |
Line & Marker | Semantics |
|---|---|---|
"inheritance" |
Solid + Hollow Triangle | Subclass generalizes superclass |
"realization" |
Dashed + Hollow Triangle | Concrete class implements interface |
"composition" |
Solid + Filled Diamond | Strong lifecycle ownership (1..*) |
"aggregation" |
Solid + Hollow Diamond | Shared collection reference |
"association" |
Solid Line / Arrow | Structural field link |
"dependency" |
Dashed + Open Arrow | Method/parameter dependency |
StateDiagram)StateDiagram models finite state automata, protocol lifecycles, and distributed job orchestrators:
State(name, shape="box", entry=..., do=..., exit=...):
"box", "ellipse", "circle", "double_circle", "text_only") and internal action compartments (entry / ..., do / ..., exit / ...).InitialState(): Solid black entry circle.ChoiceState(name="..."): Dynamic conditional diamond branch.ForkJoinState(orientation="vertical" | "horizontal", length=...): Solid synchronization bar for concurrent splits and joins.FinalState(): Bullseye termination target.sd.connect)sd.connect(src, dst, event="...", guard="...", action="...", bend=0.25) automatically formats event [guard] / action labels and supports curved arcs (bend) and self-loops (side="top").drawlib.graph -> get_layout())Manual (x, y) coordinates give total precision, while black-box tools like Graphviz lock you out of custom icons and styles. drawlib.graph bridges both worlds as a pure coordinate calculator (get_layout() only):
drawlib.graphlayout = g.get_layout()):
.node(id, width=..., height=...), .edge(src, dst), and .cluster(id, nodes=[...]).layout = g.get_layout(width=..., height=..., margin=...) to compute a GraphLayout.print(layout) or fine-tune positions with layout.offset("canary", dy=-2.5).diagrams or shapes:
layout["id"].xy, .local_xy, .bottom_left, .local_bottom_left, or edge.points directly into ArchitectureDiagram, FlowDiagram, or drawlib.shapes / drawlib.lines.bubblespeech) or charts to exact solved coordinates!Instead of forcing one generic algorithm onto every graph, drawlib.graph provides 5 domain-tuned layout solvers sharing the BaseGraph.get_layout() API:
ArchitectureGraph (Compass + Nested Packing):
parent="vpc") and positions top-level zones via compass coordinates (pos="left" | "center" | "right" | "top" | "bottom").LayerGraph (Sugiyama Hierarchical DAG):
.tier() rank pinning, and barycenter edge-crossing minimization.TreeGraph (Reingold-Tilford Compact Tree):
.child(parent, child) so sibling branches never overlap.RadialGraph (Concentric Hub & Spoke):
hub at ring=0, allocates angular sectors across concentric rings, and exposes radii via layout.ring_radii.GridGraph (2D Matrix + Smart Channel Routing):
(row, col) with .cluster_row() / .cluster_column() and routes non-adjacent edges through inter-cell channels.drawlib.anim.Animation)Drawlib compiles multi-frame APNG (.png) and Animated WebP (.webp) directly from pure Python loops—with zero external video encoders required.
from drawlib.anim import Animation
from drawlib.canvas import clear, save, setup
from drawlib.shapes import circle
from drawlib.styles import Styles
clear()
setup(width=100, height=40)
anim = Animation(fps=8.0, loop=0)
for idx, x in enumerate([20, 40, 60, 80]):
is_last = idx == 3
with anim.frame(duration=2.0 if is_last else None):
circle((x, 20), radius=6, style=Styles.PrimaryFlat)
save("motion.png") # APNG or Animated WebP
clear=True default): Each with anim.frame(): clears the canvas and captures a clean snapshot on exit.duration=2.0) so viewers can absorb the completed state before looping.Chapter 5: Multi-Frame Animations — Animation Context Manager & Encoding
By computing coordinates (x, y) or interpolating colors via get_intermediate_colors() across frames, primitive shapes illustrate live request flows, heartbeats, and state transitions.
from drawlib.anim import Animation
from drawlib.canvas import clear, save, setup
from drawlib.shapes import circle, rectangle
from drawlib.styles import Styles
clear()
setup(width=98, height=84)
anim = Animation(fps=8.0, loop=0)
# Interpolate packet X across 4 microservice hops
hops = [(16, "Client"), (38, "API Gateway"),
(60, "Worker Pool"), (82, "Database")]
for pkt_x, active_hop in packet_schedule:
with anim.frame(duration=0.14):
# Redraw static topology + active node highlight
# Draw traveling request packet at (pkt_x, 46)
circle((pkt_x, 46), radius=2.2, style=Styles.AccentFlat)
Styles.PrimaryFlat on the node currently processing the request while keeping idle nodes in Styles.Neutral.get_intermediate_colors(c1, c2, num=3, include_ends=True) for gradual heatmaps or status transitions.Chapter 5: Multi-Frame Animations — Animating Primitives & Packet Traffic
.show = False → True)Every element in drawlib.smartarts, drawlib.diagrams, and drawlib.graph supports a mutable .show: bool property.
# Build full topology once outside the loop
d = ArchitectureDiagram(...)
vpc = d.add(NodeGroup("Cloud VPC"), xy=(6, 8))
n1 = vpc.add(Node("1. Edge LB", width=16, height=14, ...), xy=(14, 24))
n2 = vpc.add(Node("2. App Tier", width=16, height=14, ...), xy=(42, 24), show=False)
n3 = vpc.add(Node("3. Storage", width=16, height=14, ...), xy=(70, 24), show=False)
d.connect(n1, n2, label="gRPC")
d.connect(n2, n3, label="SQL")
# Reveal progressively across frames
for step in range(3):
n2.show = (step >= 1)
n3.show = (step >= 2)
with anim.frame(duration=0.8):
d.draw()
.show Beats Omitting .add()NodeGroup, ChevronProcess, GridLayout, and LayerGraph compute full bounding boxes across all registered items—even when show=False.n3.show = False) automatically hides its incoming/outgoing edges and dangling junctions.Chapter 5: Multi-Frame Animations — Layout-Preserving Progressive Reveal
.draw_ratio & .draw_direction)All 7 chart types in drawlib.charts return mutable Series, Slice, or Task objects supporting .draw_ratio (0.0 → 1.0) and .draw_direction.
bar_s = bar_chart.add_series(
"Throughput", [42, 68, 95, 128],
style=Styles.PrimaryFlat,
draw_direction="bottom_to_top",
)
line_s = line_chart.add_series(
"P99 Latency", [38, 29, 19, 12],
style=Styles.AccentFlat,
draw_direction="left_to_right",
)
for ratio in [0.15, 0.3, 0.45, 0.6, 0.75, 0.9, 1.0]:
bar_s.draw_ratio = ratio
line_s.draw_ratio = ratio
with anim.frame(duration=2.0 if ratio == 1.0 else 0.12):
bar_chart.draw(xy=(8, 44))
line_chart.draw(xy=(8, 6))
"bottom_to_top": Bars grow vertically from baseline; RadarChart polygons expand radially outward; PieChart wedges grow from inner hole to outer radius."left_to_right": LineChart & AreaChart sweep continuously along arc length; GanttChart tasks extend across timeline; PieChart sweeps angularly.Chapter 5: Multi-Frame Animations — Quantitative Chart Growth (.draw_ratio)
click, once, pause)In slide decks, you control <canvas> animation playback directly from the ```drawlib code fence header—turning multi-frame APNGs into interactive slide builds:
```drawlib file=step_click_demo.png anim_trigger=click anim_loop=once anim_pause=(1, 2)
# 4-frame architectural walkthrough (Frames 0, 1, 2, 3)
```
| Fence Option | Shorthand / Alias | Behavior in slide.js |
|---|---|---|
anim-trigger:auto |
anim:auto |
Starts playing automatically when entering the slide. |
anim-trigger:click |
anim:click |
Holds on Frame 0 (READY) until clicked or A is pressed. |
anim-loop:infinite |
loop:infinite |
Loops continuously back to Frame 0 after the last frame. |
anim-loop:once |
loop:once |
Stops on the final frame (ENDED badge; click to reset). |
anim-pause:1,2 |
pause:1,2 |
Pauses at 0-based frame indices (PAUSED badge) until the next trigger. |
A, or click ▶ Play / Resume Animation in Presenter View (P) to step through pause points in real time across both screens.Chapter 5: Multi-Frame Animations — Interactive Slide Playback Controls
file:// Ready)<img> APNGs Fail in Slide DecksBrowsers play <img src="anim.png"> passively with zero JavaScript API to pause, step, reset on slide entry, or synchronize with Presenter View—and fetch("anim.png") fails with CORS errors when opened via file://.
<canvas> Architecture_assets.py):
drawlib build, the compiled APNG/WebP binary is Base64-encoded directly into <canvas class="drawlib-anim-canvas" data-base64="..."> alongside the file in images/.slide.js):
window.atob(base64Data) reconstructs the raw Uint8Array buffer in memory—working 100% offline via double-click (file://) without an HTTP server.ImageDecoder API extracts ImageBitmap frames + microsecond durations.acTL, fcTL, IDAT, fdAT + CRC32) composites frames onto an offscreen canvas.Chapter 5: Multi-Frame Animations — Zero-Dependency Base64 Canvas Engine
drawlib init project)Never assemble documentation directories or build scripts manually. drawlib init project scaffolds production-ready project structures with synchronized themes (styles.py + style.css) and modular build_*.sh scripts:
# 1. Multi-page documentation website with sidebar
uv run drawlib init project site -s default
# 2. Linear technical specification / RFC / whitepaper
uv run drawlib init project doc -s google
# 3. 16:9 widescreen presentation slide deck
uv run drawlib init project slide -s default
# 4. Standalone batch Python illustration repository
uv run drawlib init project images -s monochrome
<target>_src/: All human- and agent-authored Markdown, styles.py, and utils.py live in <target>_src/../<target>_src/build.sh compiles <target>_html/, <target>.pdf, <target>_markdown/, and <target>_images/.Chapter 6: Documentation & Slide Engine — Four Project Archetypes
site)A site project (docs_src/<lang>/ + drawlib.toml) compiles nested Markdown folders into both a responsive HTML documentation portal (docs_html/<lang>/) and a GitHub-browsable Markdown mirror (docs_markdown/<lang>/).
site Capabilities<lang>/navbar.md):
# Brand Title sets the top-left header; ## Section groups links into collapsible categories; every internal .md link is validated at build time.show-code / fold-code):
show-code to a ```drawlib fence renders an interactive UI card allowing readers to toggle between the Rendered Diagram and Copyable Python Source.drawlib serve --check):
scan_broken_links() verifies all internal <a href> links, section anchors, and <img src> assets before launching the live preview server.Chapter 6: Documentation & Slide Engine — Multi-Page Documentation Sites (site)
doc & pdf)The doc archetype (doc_src/) is purpose-built for linear technical documents—such as architecture RFCs, whitepapers, and quickstart manuals:
doc_src/
├── drawlib.toml # [project] + [i18n] manifest
└── en/ # Locale chapter tree (-l en,ja)
├── 00_cover.md # Title, subtitle, author metadata
├── 01_overview.md # Chapter 1
└── 02_design.md # Chapter 2 (with ```drawlib blocks)
doc_html/<lang>/index.html):
.md chapters in sorted order into a per-locale index.html with an auto-generated Table of Contents, a centered 900px reading container, and full file:// portability.doc_<lang>.pdf / .docx):
drawlib build pdf --toc renders crisp vector A4 PDFs via headless Chromium, while drawlib build docx exports native editable MS Word documents.Chapter 6: Documentation & Slide Engine — Single-Page Technical Docs & Vector PDFs
1920x1080)Drawlib slides (drawlib init project slide) combine pixel-accurate stage containers with Cartesian vector canvases:
::: block xy=(x, y) width=W height=H) — Top-Left (0, 0):
00_opening/, 01_why_drawlib/, ...) sorted alphabetically.setup(width=W, height=H)) — Bottom-Left (0, 0):
```drawlib block, coordinates use standard Cartesian geometry where (0, 0) is the bottom-left corner.Keep setup(width=W, height=H) proportional to the enclosing ::: block width and height (typically width / 10, height / 10) so inline SVGs fill their containers with zero letterboxing:
::: block xy=(860, 140) width=980 height=840 → setup(width=98, height=84)::: block xy=(0, 0) width=1920 height=1080 → setup(width=192, height=108)Chapter 6: Documentation & Slide Engine — 16:9 Widescreen Stage Architecture
Traditional Markdown-to-slide tools lock you into centered bullet lists. Drawlib gives you absolute pixel coordinates plus z_index=<layer> stacking:
<!-- Layer 1: Right-half full-bleed diagram (Y: 0..1080) -->
Floating Callout (::: block ... z_index=15)
Live Floating Overlay (::: block xy=(760, 740) width=520 height=165 z_index=15)
X: 80..840) and right full-bleed SVG (X: 920..1920).rgba slate background, indigo border, and drop shadow).Chapter 6: Documentation & Slide Engine — Spatial Freedom & Z-Index Layering
styles.py & utils.pyMaintaining visual consistency across a 60+ slide deck in GUI tools is tedious and error-prone. Drawlib solves this with two shared Python modules in slide_src/:
styles.py — Global Design Tokensfrom drawlib.preset_colors import DefaultColors
from drawlib.preset_styles import DefaultStyles
Colors = DefaultColors() # Or GoogleColors, MonochromeColors
Styles = DefaultStyles() # Or GoogleStyles, MonochromeStyles
Switching a 64-slide presentation from DefaultStyles to GoogleStyles or MonochromeStyles is a two-line change in styles.py.
drawlib.slide & utils.py — Reusable Slide Macrosdrawlib.slide.draw_page_number(): Renders current_slide.text ("42 / 64") on a transparent canvas (alpha=0.0).drawlib.slide.draw_section_header(...): Full-bleed 16:9 chapter cover (split, centered, banner, minimal) with progress dots and topic cards.utils.py Domain Macros: Project-specific helpers (service_card(), connect(), draw_kpi_cards()) callable in 3 lines of Python.Chapter 6: Documentation & Slide Engine — Project-Wide Consistency (styles.py & utils.py)
Compiled Drawlib slide decks (slide_html/) include a zero-dependency presentation runtime (slide.js) and self-contained assets:
| Shortcut | Feature | Description |
|---|---|---|
P / S |
Presenter View | Opens dual-window ?presenter=1 synced via BroadcastChannel + postMessage. |
O / Esc |
Overview Grid | Interactive 16:9 thumbnail grid of all slides for instant navigation. |
A |
Animation Step | Plays, pauses, resumes, or resets <canvas> animations on the current slide. |
L |
Language Switch | Cycles multi-locale decks (<lang>/index.html#N) preserving slide number. |
F |
Fullscreen | Toggles native widescreen browser fullscreen mode. |
→ / Space |
Next Slide | Advances slides (← / Home / End for navigation). |
file://, PDF & PowerPoint Portability_assets/fonts/): Copies used .ttf/.otf text & icon fonts and injects @font-face rules into style.css.<lang>/index.html directly from disk (file://) or export slide_<lang>.pdf / .pptx.Chapter 6: Documentation & Slide Engine — Presenter View, Font Bundling & Portability
drawlib rules)Why do AI coding agents struggle with niche libraries? Because their training data is outdated or incomplete. Drawlib solves this by shipping its complete AI agent manual inside the Python package:
# 1. List all 92 embedded rule manuals
uv run drawlib rules list
# 2. Core orientation & design discipline
uv run drawlib rules show overview
uv run drawlib rules show style-guide
uv run drawlib rules show review-guide
uv run drawlib rules show api
# 3. Project archetypes & module deep-dives
uv run drawlib rules show project-overview
uv run drawlib rules show project-slide
uv run drawlib rules show anim-guide
uv run drawlib rules show lib-diagrams-overview
uv run drawlib rules show lib-charts-overview
.agents/rules/drawlib.md bootstrap and query specific lib-<module> manuals on demand—keeping LLM context windows lean and 100% version-accurate.Chapter 7: AI Agent Workflow & Ecosystem — Built-in AI Agent Knowledge Base
Code that runs without syntax errors can still produce overlapping labels or cramped margins. Drawlib pairs headless coordinate-grid rendering (-g) with multimodal agent vision (view_file):
# Render a single diagram block with a 10x10 coordinate grid
uv run drawlib show docs_src/arch.md service_arch.png \
-g -o .drawlib/scratch/preview.png
drawlib rules show): Load coordinate & style rules.-g): Export headless preview image in < 1s.view_file): Agent visually inspects the rendered PNG + coordinate grid lines.(x, y) coordinates, padding, or contrast if any clipping is spotted.Chapter 7: AI Agent Workflow & Ecosystem — Autonomous Visual Self-Healing Loop
A comprehensive technical site or 64-slide presentation can contain 100+ embedded diagrams and multi-frame animations. Re-executing every Python block on every edit would slow down live authoring.
.drawlib/cache.sqlite3)Drawlib hashes four inputs before executing any ```drawlib block:
styles.py & utils.py Hashes: Editing a shared helper or theme invalidates dependent diagrams automatically..svg, .png (APNG), or .webp.total_slides): Ensures current_slide.text (12 / 64) stays accurate when slides are added or removed.Chapter 7: AI Agent Workflow & Ecosystem — Sub-Millisecond SQLite Build Cache
The drawlib repository is simultaneously the library codebase (src/drawlib/) and a live production dogfooding environment where every project archetype is continuously built and tested via ./dcli:
# 1. Static analysis (Ruff lint/format, Ty type check, docstrings)
./dcli code-check all
# 2. Parallel unit & integration test suite
./dcli test all
# 3. Build all 6 live dogfooding documentation targets
./dcli docs build --all
./dcli docs serve site --check
docs/site (docs/docs_src/): Bilingual (en/ja) documentation website (docs_html/ & docs_markdown/).quickstart (docs/quickstart_src/): Quickstart guide & vector PDF (doc archetype).dogfooding (docs/drawlib-dogfooding_src/): Bilingual (en/ja) architectural whitepapers (doc + _en.pdf/_ja.pdf).slide (docs/slide_about_drawlib_src/): This 64-slide 16:9 showcase presentation deck!readme (docs/readme_src/): Standalone hero illustrations for README.md.Chapter 7: AI Agent Workflow & Ecosystem — Live Repository Dogfooding
drawlib)From initial project scaffolding to headless visual inspection and PDF / Office publishing, every workflow runs via uv run drawlib:
uv run drawlib init project <site|doc|slide|images>
uv run drawlib init agent <claude|cursor|antigravity>
uv run drawlib rules show overview
# Render single block with coordinate grid
uv run drawlib show page.md arch.png \
-g -o .drawlib/scratch/preview.png
# Check links & preview in browser
uv run drawlib serve docs_html/ --check
./docs_src/build.sh
# Or export HTML, PDF, Word & PowerPoint:
uv run drawlib build html docs_src/ -o docs_html/
uv run drawlib build pdf doc_src/ -o doc.pdf
uv run drawlib build docx doc_src/ -o doc.docx
uv run drawlib build slide slide_src/ -o slide/
uv run drawlib build pptx slide_src/ -o deck.pptx
Chapter 7: AI Agent Workflow & Ecosystem — Complete CLI Cheat Sheet
Stop context-switching between Git, GUI drawing tools, wikis, and slide editors. Bring your entire visual communication stack into version-controlled Python and Markdown.
# 1. Install Drawlib (PDF, DOCX & PPTX built in)
uv add drawlib # or: pip install drawlib
# 2. Scaffold a 16:9 slide deck, doc, or site
uv run drawlib init project slide my_deck -s default
# 3. Build HTML + PDF (auto Chromium) & preview
./my_deck_src/build.sh
./my_deck_src/serve.sh
build pdf), MS Word (build docx), and 16:9 PowerPoint (build pptx) work out of the box—headless Chromium auto-downloads on first build (cache download --all / --browser for CI; playwright install-deps chromium on minimal Linux containers).drawlib init agent, drawlib rules, and headless grid review (drawlib show -g).https://github.com/yuichi110/drawlibChapter 7: AI Agent Workflow & Ecosystem — Thank You & Getting Started