Block diagram
About block diagrams
A block diagram models a system as a set of functional blocks connected by directed signal lines. Each block represents a component whose behavior is captured by a transfer function or a descriptive label; signals flow through the blocks in a defined direction. Control engineers use them to design feedback loops, signal-processing engineers use them to document filter chains, and systems engineers use them to decompose complex architectures. The defining visual elements — rectangular blocks and circular summing junctions — are conventions drawn from control systems analysis (Ogata, Franklin, Nise) and carried into every datasheet and textbook that describes a feedback system.
Schematex follows the Laplace-domain transfer-function convention used in control systems textbooks: blocks are labeled with their transfer functions, summing junctions carry explicit +/− polarity signs, and a discrete flag on signals renders a dashed line for sampled-data paths. This page documents what the parser accepts today. Authoritative references: Ogata (2010) Modern Control Engineering; Franklin, Powell & Emami-Naeini (2018) Feedback Control of Dynamic Systems.
1. Your first block diagram
The smallest useful block diagram: one controller, one plant, one feedback loop.
Four rules cover 80% of usage:
- Start with
blockdiagram, optionally followed by a quoted title. - Declare each component with
ID = block("label"), each summing junction withID = sum(+a, -b), and each named signal withID = signal("label"). - Connect components with
->. Chain multiple hops on one line:A -> B -> C. - Optionally annotate connections with a trailing label:
A -> B ["E(s)"].
Comments must start with
#on their own line.
2. Blocks
A block represents any functional element — controller, plant, filter, actuator, sensor. The label is typically a transfer function or a descriptive name.
Syntax: ID = block("label") [role: X]
| Attribute | Values | Effect |
|---|---|---|
role: plant | plant, controller, sensor, actuator, reference, disturbance, generic | Visual color coding; generic is the default |
route: above | above, below | Routing hint for feedback and feedforward blocks |
ID rules. Must start with a letter or underscore, followed by letters, digits, or underscores: [A-Za-z_]\w*.
3. Summing junctions
A summing junction combines multiple signals into one, with explicit polarity for each input. It renders as a circle with +/− signs — the standard control-systems symbol.
Syntax: ID = sum(+a, -b, +c, …)
- Each input is a signed ID:
+xadds signalx,-ysubtracts signaly. - An input without a sign is treated as positive.
- The summing junction ID is then used as the target of connection lines, just like a block ID.
4. Signals
A signal declaration creates a named signal node that the parser inlines as an edge label. Signals are pass-through: when the parser sees A -> sig and sig -> B, it merges them into a single edge from A to B, labeling it with the signal's display text.
Syntax: ID = signal("label") [discrete]
- Omit
[discrete]for continuous signals (solid line). - Add
[discrete]for sampled-data signals (dashed line).
e_sig = signal("E(s)")
u_sig = signal("U(s)") [discrete]Signals are purely a labeling convenience — you can also label edges directly with a trailing attribute (see §5).
5. Connections
A connection line is from -> to. The -> operator always produces a directed, arrowed line.
Single hop: A -> B
Chain: A -> B -> C — equivalent to A -> B and B -> C. Both are written in one line.
With a signal label: append ["label text"] at the end of the chain. The label applies to the last hop only.
ctrl -> plant ["U(s)"]With a discrete flag: append [discrete] to make the last-hop arrow dashed.
plant -> adc ["y"] [discrete]Both label and discrete: use [label: "Y(s)", discrete] (comma-separated).
adc -> ctrl [label: "y[k]", discrete]6. Labels & comments
- Title:
blockdiagram "My System"— first line, quoted. - Block label: the quoted string inside
block("…")— appears inside the box. - Signal label: the quoted string inside
signal("…")— appears on the merged edge. - Edge label: trailing
["text"]or[label: "text"]on a connection line — appears on that arrow. - Comments:
#at the start of a line (after leading whitespace). Inline trailing comments are not supported.
7. Reserved words & escaping
Reserved at line start: blockdiagram (header).
Structural keywords (avoid as block/signal/sum IDs to prevent ambiguity): block, signal, sum.
in and out are conventional IDs for the external boundary of a diagram — the parser treats them as ordinary identifiers, but the renderer uses them as implicit source/sink nodes. Using in -> r and plant -> out is idiomatic.
Strings with spaces must be double-quoted in block("…") and signal("…") labels.
8. Common mistakes
| You wrote | Parser says | Fix |
|---|---|---|
G = block(G(s)) (no quotes) | Parse fails — label must be quoted | G = block("G(s)") |
err = sum(r, -ym) (no +) | r treated as +r — works, but ambiguous | Write sum(+r, -ym) for clarity |
ctrl -> plant, plant -> out (comma on one line) | , is not a separator — parse fails | One connection per line or use chain: ctrl -> plant -> out |
s1 = signal("E(s)") [label: "E"] | label: not valid on signal; use it on connections | Drop label: from signal declaration |
role: filter | Unknown role — silently defaults to generic | Use plant, controller, sensor, actuator, reference, disturbance, or generic |
A -> B [discrete, label: "e"] — label first fails | Order of attrs inside […] doesn't matter, but bare "text" shorthand only works when it's the only item | Use [label: "e", discrete] |
9. Grammar (EBNF)
document = header (blank | comment | block-def | sum-def | signal-def | connection)*
header = "blockdiagram" ( WS quoted-string )? NEWLINE
quoted-string = '"' any-char-but-quote* '"'
block-def = id WS "=" WS "block" "(" quoted-string ")" ( "[" block-attrs "]" )? NEWLINE
block-attrs = block-attr ("," block-attr)*
block-attr = "role:" role | "route:" ("above" | "below")
role = "plant" | "controller" | "sensor" | "actuator"
| "reference" | "disturbance" | "generic"
sum-def = id WS "=" WS "sum" "(" sum-inputs ")" NEWLINE
sum-inputs = sum-input ("," sum-input)*
sum-input = ("+" | "-")? id
signal-def = id WS "=" WS "signal" "(" quoted-string ")" ( "[" "discrete" "]" )? NEWLINE
connection = id ("->" id)+ ( "[" conn-attrs "]" )? NEWLINE
conn-attrs = quoted-string # shorthand: bare label only
| conn-attr ("," conn-attr)*
conn-attr = "label:" quoted-string | "discrete"
id = [A-Za-z_] \w*
comment = "#" any NEWLINEAuthoritative source: src/diagrams/blockdiagram/parser.ts. If this diverges from the parser, the parser wins — please open an issue.
10. Standard compliance
Schematex block diagrams follow the Laplace-domain transfer-function conventions from Ogata (2010) and Franklin et al. (2018) — the block, summing junction, and directed signal-line symbols found in every control systems textbook.
What is implemented today:
- ✅ Rectangular blocks with transfer-function labels
- ✅ Circular summing junctions with
+/−polarity inputs - ✅ Named signal nodes (pass-through, merge to edge label)
- ✅ Directed connections with chaining (
A -> B -> C) - ✅ Edge labels — signal names on arrows
- ✅
discreteflag — dashed line for sampled-data signals - ✅ Role annotations (
plant,controller,sensor,actuator,reference,disturbance,generic) - ✅
route: above | belowhint for feedback/feedforward placement - ⏳ Branch/pickoff point — explicit dot symbol where one signal fans out to two destinations
- ⏳ Boundary box — dashed subsystem enclosure with label
- ⏳ Bidirectional arrows —
<->for two-way signal exchange - ⏳ Bus notation — thick line representing a vector of signals
References:
- Ogata, K. (2010). Modern Control Engineering, 5th ed. Prentice Hall.
- Franklin, G.F., Powell, J.D. & Emami-Naeini, A. (2018). Feedback Control of Dynamic Systems, 8th ed. Pearson.
11. Related examples
12. Roadmap
Planned — not yet parseable. Do not use these in generated DSL today; the parser will reject or ignore them.
- Branch/pickoff point symbol — explicit
dotprimitive where one output fans to multiple destinations (currently the renderer auto-inserts a branch when an ID is used as a source more than once, but there is no explicit syntax). - Subsystem boundary box —
boundary "label" { … }block that draws a dashed rectangle enclosing the contained blocks, used for multi-loop and subsystem views. - Bidirectional connection —
A <-> Bfor components with mutual information exchange (e.g. a bus interface). - Bus (vector signal) notation — thick line with a slash-and-count annotation
//nindicating an n-wide signal bus. - Nested subsystems — a
blockwhose interior is itself a block diagram, collapsible in the rendered output.
Track in the GitHub issues if you need any of these sooner.