When I explain a system like the AOSP camera stack, a 2D diagram is not enough. The app calls CameraManager, Binder takes the call to cameraserver, the camera service talks to the HAL, and the HAL drives a V4L2 driver in the kernel. At the same time, frames come back through gralloc buffers and SurfaceFlinger. On a whiteboard this becomes many boxes and crossing arrows. You lose the layers, or you lose the links.

I wanted a better way to see this for a long time, so I built Archi3D. You describe your system in a small text file: the elements, their ports and the links between them. Archi3D shows it in 3D. Containers are levels on top of each other, so you can see the nesting, and you can put whole systems on their own floors, like the layers of AOSP. You can rotate, zoom in and click on things. It is open source, and it works best inside VS Code.

Start in VS Code

Archi3D is a VS Code extension. Your architecture is a text file next to your code, and the 3D view is next to the file. You can review it in a pull request like any other file. The picture is always up to date, because it is drawn from the text every time.

  • Install it. Search for Archi3D in the Extensions view, or run code --install-extension M-H-H.archi3d.
  • Create a file with the extension .arch3d and describe a few parts of your system. The next section shows how.
  • Open the preview. Use the button in the editor title, Ctrl+K V (Cmd+K V on Mac), or Archi3D: Open 3D Preview to the Side in the command palette.

What the extension does:

If you prefer the browser, the same app also runs as a web page from the GitHub repository.

How it works

A model is a small text file. One line for each element, one line for each link, and { } for nesting. This is a part of the AOSP camera example in the repository:

system NativeServices "Native services" {
  container cameraserver {
    service CameraService   in: connect:AIDL   out: hal:AIDL, cb
    service Camera3Device   in: submit:AIDL    out: hal:AIDL, buffers
  }
}
system HAL {
  library CameraDeviceHal "ICameraDeviceSession"  in: session:AIDL, frames  out: ioctl
  library Gralloc         in: alloc
}

CameraManager.binder  -> CameraService.connect : connectDevice()
Camera3Device.hal    <=> CameraDeviceHal.session : processCaptureRequest
CameraDeviceHal.ioctl -> V4L2Driver.ioctl : VIDIOC_QBUF
V4L2Driver.frames     >> CameraDeviceHal.frames : frames
CameraDeviceHal       <> Gralloc : gralloc buffers

The keyword (service, library, driver, device, database, topic…) only picks the 3D shape. in: is what an element provides and out: is what it needs. A port can have a type, for example AIDL. The arrow says how the two sides talk, and each kind has its own line style:

ArrowMeaningAOSP example
->call and wait for the answerAIDL call, ioctl
=>send a message, don’t waitoneway AIDL, explicit Intent
~>event for anyone who listensbroadcast, callback
<=>session that stays openbound service, HAL session
>>data that keeps flowingcamera frames, audio
<>shared memory or datagralloc buffers, ashmem

Inside, Archi3D reads the file, builds a model and lays it out in 2D with ELK. ELK is a layout engine that understands nested boxes and ports. Then every nesting level goes one floor higher, and three.js draws it in the browser. There is no game engine.

When you zoom out, the containers close and you only see the links between them. A container opens again as soon as what is inside it is big enough to read, the biggest ones first, so you keep the big picture while you zoom in. Click a closed container to open it where it is, and use + and - for more or less detail.

Layers and colours

New in version 0.3.0. Put your systems on floors with layer blocks. The first layer you write is the top floor:

layer Apps "Applications" {
  system Apps { ... }
}
layer Native "Native services" {
  system NativeServices { ... }
}
layer Kernel "Linux kernel" {
  system Kernel { ... }
}

Your systems stay as they are, a layer only says where they stand. Links between floors go straight up or down, so a camera call goes down the stack in front of you. When you come close to one floor, the floors above it turn into faint outlines, so they don’t cover it.

You can also give elements and links your own colours, one by one or for many at once:

driver V4L2 "V4L2 driver" [color: #2e7d6b]
style kind driver [color: slate]
style tag vendor  [color: #e8871e]
style link event  [color: purple]

A #rrggbb colour is used exactly as you write it. Names like teal or slate change with the light and dark theme.

Why 3D?

3D has a bad name in visualization. Things hide behind other things, and perspective makes sizes hard to compare. But the research says more than “3D is bad”. Mitchell and Ware found that people could follow paths in 3D graphs about ten times bigger than in 2D, but only when they could move the view (Mitchell & Ware, 2008). In an experiment with 41 developers, the 3D tool CodeCity gave 24% more correct answers in 12% less time than an IDE and a spreadsheet (Wettel, Lanza & Robbes, ICSE 2011).

So two things matter: the depth must mean something, and you must be able to move. This is how Archi3D does it:

  • Height means something. Every container level is one floor higher than its parent, and with layers every layer has its own floor.
  • You move the view. Rotating is what makes the depth easy to read, so drag, zoom and fly in are the main way to use it.
  • Distance decides the detail. From far away you see a few closed blocks. As soon as what is inside a block is readable, it opens. This is called semantic zoom.
  • Selection hides the noise. Click an element, and it and its direct links stand out with an orange outline while the rest fades. Click a container, and you see the links that cross its border.
  • Links can be bundled. With bundling on, links between closed containers follow the nesting tree, so a busy overview shows a few clear bundles.

Built in small steps

Archi3D is written in TypeScript and runs in the browser. The steps are simple: read the text, build a model, lay it out in 2D, lift it to 3D and draw it. Everything before the drawing is plain data, so every step has its own unit tests. The 3D view is tested with screenshots from fixed camera positions, on every pull request.

I built it in 15 small steps. A step was done only when the tests passed and the screenshots looked right. The build plan is in the repository. It also works offline: no CDN and no network calls. A test model with 1,000 elements lays out in under a second and is drawn with about ten draw calls.

What’s next

The current version is 0.3.0. Next on the list:

  • A better editor with completion and go to definition, by moving the language to Langium.
  • Delegated interfaces: a container can pass one of its ports to the element that implements it.
  • Linked elements on different layers placed above each other, so the links between floors are short.

Archi3D is MIT licensed and on GitHub. If you work on AOSP, Android apps or Linux, try to model a part of your system and tell me where the language or the view is not good enough. Issues and pull requests are welcome.

Try it

Install Archi3D from the VS Code Marketplace, create a .arch3d file and open the preview. The setup, a first model, and the full reference for the language, the controls and the commands are on the next page:

Getting started and reference