This page describes the .arch3d language as of Archi3D 0.3.0. It is a small language with one statement on each line. You describe elements, their ports, and the links between them. Every rule here comes from the reference in the repository. All keywords are also on one page.

At a glance

# A comment. // works too.
kind Name ["Title"] [[color: #2e7d6b]] [in: port, port:Type] [out: port]   # an element
kind Name ["Title"] [in: ...] [out: ...] {                                 # a container
  ...                                                                      # anything, nested
}                                                                          # closes the last container
layer Name ["Title"] { ... }                                               # a layer: its own floor
From[.port] -> To[.port] [: label]                                         # a link
From[.port] ~> To1, To2 [: label]                                          # one link to several targets
From[.port] event To[.port] [: label]                                      # a link written with a word
From[.port] -> To[.port] [[color: red]] [: label]                          # a link with its own colour
style kind driver [color: slate]                                           # a colour for many at once

File basics

  • Files use the extension .arch3d.
  • One statement on each line: a comment, an element, a container, a closing }, a link or a style line.
  • # or // starts a comment until the end of the line. It does not start a comment inside a quoted title, or inside [ ] where # starts a colour like #2e7d6b.
  • Empty lines and indentation are ignored. Indent for readability.
  • The order of the lines doesn’t matter. A link may come before the elements it uses.
  • A mistake doesn’t stop the file. The parser skips the bad line and goes on, so the 3D view still shows everything that is valid.

Elements

kind Name ["Title"] [[attributes]] [in: port, port...] [out: port, port...]
user     Person   "Phone user"
app      Camera   "Camera app"    out: bind
service  CamSvc   "CameraService" in: aidl
database MediaDB
driver   V4L2     "V4L2 driver" [color: #2e7d6b]   in: ioctl
  • kind picks the 3D shape and nothing else. The keywords are in the next section. Any other word is allowed and is drawn as a plain box.
  • Name starts with a letter or _, then letters, digits, _ or -. It must be unique in the whole file, also across containers.
  • Title is optional text in double quotes. It is shown as the label instead of the name, so the name can stay short. A title may contain spaces and #. Write \" for a quote and \\ for a backslash.
  • Attributes are optional, in [ ] after the name or title. See Colours.
  • Links always use the name, never the title.

Element keywords

The list is short on purpose. The same words work for Android apps, AOSP and Linux.

KeywordDrawn asAndroid appAOSPLinux
user (or actor)a personthe phone usera developer with adba user at the shell
uia screenActivity, Fragment, Compose screenSystemUI, SettingsGTK or Qt window, terminal
appa phonethe app (APK)Camera, Launcherdesktop application
servicea boxService, WorkManager workersystem_server services, native daemonsdaemon, systemd unit
librarystacked layersAAR, Jetpack libraryHAL implementation, libbindershared library (.so)
drivera chipkernel driver under a HALkernel module
kernela flat octagonLinux kernel, binder driverkernel, a subsystem
devicea box on feetsensorcamera sensor, modemUSB device, hardware
database (or db)a cylinderRoom, SQLiteMediaProvider databasePostgreSQL, SQLite
cachea hexagonLruCachethumbnail cacheRedis, page cache
storagestacked disksfiles, SharedPreferences/data, /sdcardfile system, block device
queuea low boxHandler and MessageQueueLooper queuemessage queue
topica pipebroadcast, Flowbroadcast Intents, event logD-Bus signals, MQTT topic
externala see-through boxyour backend servera vendor part outside AOSPa remote host

Other words such as component, system, container, group or domain are drawn as plain boxes. They are good names for containers.

Ports

Ports are the interfaces of an element.

service Orders   in: api, events   out: db, pay
service Gateway  out: orders:REST, search:gRPC
  • in: lists what the element provides. These ports are drawn on the left side, in teal.
  • out: lists what the element needs. These ports are drawn on the right side, in red.
  • A port can have a type after a colon: orders:REST. If both ends of a link have a type and the types differ, you get warning W201.
  • Ports are optional. You can declare an element with no ports and still link it.

Containers

A line that ends with { opens a container. } closes the last one that is open.

system Shop "Online shop" {
  app Mobile "Shop app"
  container Backend {
    service  Orders   in: api   out: db
    database OrderDB  in: db
  }
}
  • A container can hold elements and other containers, as deep as you need.
  • Every nesting level is drawn one floor higher in 3D.
  • Any word can be the kind of a container. system, container and domain are common. Only layer is special, see the next section.
  • A container can have a title, attributes and ports, like an element.
  • Several } can share one line: } }.
  • Links can point at a container: Frontend -> Backend : REST.

Layers

A top-level container with the kind layer is drawn as its own floor. The first layer in the file is the top floor, the next one is below it, and so on. To change the order, move the blocks.

layer Apps "Applications" {
  system Apps { ... }
}
layer Framework "Java framework" {
  system Framework { ... }
  system SystemServer { ... }
}
layer Kernel "Linux kernel" {
  system Kernel { ... }
}
  • Keep your systems as they are and put them inside layers. A layer only says where they stand in the stack. A floor can hold several systems.
  • Links between floors go straight up or down.
  • An element outside every layer, like a user or a device, stands next to the floor it links to most.
  • A model with layers opens with a low camera from the side. When you come close to one floor, the floors above it turn into faint outlines and only the labels of that floor are shown.
  • A layer must be at the top level and have { }. Otherwise you get warning W203 and it is drawn as a normal container or element.

Links

From[.port] arrow To[.port] [, To[.port] ...] [[attributes]] [: label]
From[.port] word  To[.port] [, To[.port] ...] [[attributes]] [: label]
User -> Mobile : tap
Mobile.api -> Orders.api : HTTPS
Orders ~> Mail, Analytics : OrderPlaced
Orders.db call OrderDB.db
OrderDB <- Orders
Orders -> Payments [color: red] : charge
  • Ports are optional. Without a port, the link uses the first port of the right direction. If there is none, a port without a name is made.
  • Undeclared ports are made for you. A.x -> B.y creates x as an out port on A and y as an in port on B if they don’t exist.
  • Label is everything after : until the end of the line.
  • Several targets. A ~> B, C, D : label makes three links with the same kind, label and colour.
  • Right to left. A <- B is the same as B -> A. It takes only one target.
  • Names, not paths. Write OrderDB, not Shop.Backend.OrderDB.
  • Attributes on a link come after the targets and before the label.
  • Words as names. An element can still be named like a link word: service event in: x declares an element called event.

Link kinds

There are six kinds. Pick by how the two sides talk, not by the technology.

ArrowWordMeansDrawn asAndroid and AOSPLinux
-> (or -->)callask and wait for the answersolid linemethod call, AIDL call, Binder transactionfunction call, syscall, ioctl, D-Bus method
=>messagesend, don’t wait, one receiverdashed lineexplicit Intent, Handler.post, oneway AIDLmessage queue, socket send
~>eventtell anyone who listensdotted linebroadcast Intent, LiveData or Flow, callbackssignal, uevent, D-Bus signal, inotify
<=>sessiona connection that stays open, both waystwo thin lines, arrows at both endsbound service with AIDL, HAL with callbacksUnix socket, TCP connection
>>streamdata that keeps flowingthick line with flow markscamera frames, audio, sensor datapipe, V4L2 buffers, ALSA
<>sharedboth use the same memory or datawide see-through band, squares at the endsashmem, gralloc buffers, a shared fileshared memory, mmap

message, event and stream count as asynchronous. The other three are synchronous.

Colours

There are three ways, from one element to many.

One element, container or link, with [ ]:

driver V4L2 "V4L2 driver" [color: #2e7d6b]
system HAL [color: #fde7c8, dark: #3d3326] { ... }
CameraService -> Gralloc [color: red] : alloc

Many at once, with a style line:

style kind driver   [color: slate]      # every driver
style tag vendor    [color: #e8871e]    # everything with [tag: vendor]
style link event    [color: purple]     # every event link

Without writing anything, with the Colour button in the app and in VS Code: colour by layer, by top container or by tag, with a legend.

AttributeValueMeans
color#rrggbb, #rgb or a colour namethe colour
darkthe samethe colour in the dark theme (without it: the same as color)
taga namea tag, can repeat: [tag: vendor, tag: camera]
  • A #rrggbb colour is used exactly as you write it, in the light and in the dark theme.
  • The names change with the theme: red, orange, amber, green, teal, blue, indigo, purple, pink, brown, slate, gray.
  • A container gets a tinted plate, not a solid one.
  • Which colour wins: the element’s own, then a style tag, then a style kind. For links: the link’s own, then a style link.
  • A selected element gets lighter with an orange outline, so the selection never clashes with your colours.

Errors and warnings

The codes are stable, so you can search for them. Every message has a line and a column. Errors skip the line, warnings keep it.

CodeTypeWhenHow to fix it
E001errorThe line is not a valid statement. Also a title with no closing quote, a [ with no ], or a trailing comma.Check the line against the forms on this page
E002error} but no container is openRemove the } or add the missing {
E003errorA container is never closed. Shown on the line where it starts.Add the }
E101errorA name is defined twiceRename one of them
E102errorA link uses a name that doesn’t existCheck the spelling, or add the element
E103errorAn element links to itselfRemove the link
W201warningBoth ports have a type and the types differMake the types the same
W202warningAn in port is used as a link source, or an out port as a targetSwap the link, or move the port to the other list
W203warningA layer is inside a container or has no { }Put it at the top level with { }
W204warningAn unknown attribute in [ ], or a value that is not a colourUse color, dark or tag, and #rrggbb, #rgb or a colour name
W301warningImport: a Mermaid line that is not supported was skippedNothing to do, or draw that part in .arch3d

Importing Mermaid and D2

Archi3D reads Mermaid architecture-beta files (.mmd) in the web app and in VS Code. The web app also reads D2 files (.d2). An imported file is turned into the same model as an .arch3d file.

Mermaid architecture-beta

MermaidBecomes
group id(icon)[Title] in parenta container
service id(icon)[Title] in parentan element. The icon picks the kind: database and disk → database, internet → external, server and cloud → service
a:R --> L:b (also -- and <--)a call. The sides L, R, T, B become the port sides
a:R <--> L:ba session
anything else, such as junctionskipped with warning W301
architecture-beta
    group shop(cloud)[Shop]
    service web(internet)[Web] in shop
    service api(server)[API] in shop
    service db(database)[Database] in shop
    web:R --> L:api
    api:R --> L:db

D2 (web app only)

D2Becomes
a shape with childrena container
shape: cylinder or stored_datadatabase
shape: queuequeue
shape: person or c4-personuser
any other shapea plain box
arrows at both endssession
dashed connectionmessage
any other connectioncall

A complete example

# A photo is taken and saved
style kind driver [color: slate]

user User "Phone user"
system Phone {
  app Camera "Camera app"        in: done   out: open
  container Framework {
    service CameraService        in: connect   out: hal, saved
    database MediaDB "Media DB"  in: save
  }
  library CameraHal "Camera HAL" [tag: vendor]  in: session   out: ioctl
  driver V4L2 "V4L2 driver"      in: ioctl
}
external Cloud "Photo cloud"     in: upload

User               -> Camera : tap
Camera.open        -> CameraService.connect : openCamera()
CameraService.hal <=> CameraHal.session : HAL session
CameraHal.ioctl    -> V4L2.ioctl : VIDIOC_QBUF
V4L2              >> CameraHal : frames
CameraService      => MediaDB.save : insert
CameraService.saved ~> Camera.done, Cloud.upload [color: purple] : NEW_PICTURE

Grammar (EBNF)

file       = { line } ;
line       = ws [ statement ] [ comment ] newline ;
statement  = close | node | edge | style ;
close      = "}" { ws "}" } ;
node       = kind ws name [ ws title ] [ ws attrs ] { ws portlist } [ ws "{" ] ;
title      = '"' { char except '"' and "\" | "\" char } '"' ;
attrs      = "[" [ attr { "," attr } ] "]" ;
attr       = ("color" | "dark") ":" colour | "tag" ":" ident ;
colour     = "#" hex hex hex [ hex hex hex ] | colourname ;
portlist   = ("in:" | "out:") ws port { ws? "," ws? port } ;
port       = ident [ ":" ident ] ;
edge       = ref ws? link ws? ref { ws? "," ws? ref } [ ws attrs ] [ ws ":" ws label ] ;
link       = arrow | ws linkword ws ;
arrow      = "->" | "-->" | "=>" | "~>" | "<=>" | ">>" | "<>" | "<-" ;
linkword   = "call" | "message" | "event" | "session" | "stream" | "shared" ;
style      = "style" ws ("kind" | "tag" | "link") ws ident ws attrs ;
ref        = ident [ "." ident ] ;
kind       = ident ;  name = ident ;
ident      = letter { letter | digit | "_" | "-" } ;
label      = { any character except newline } ;
comment    = ("#" | "//") { any character except newline } ;   (not inside quotes or [ ])

Not in the language yet

  • Delegation: port => Child.port inside a container, to pass a container’s port to the element that implements it.
  • More attributes, for example [team: payments].