Tutorial
▞ Introduction
In this tutorial we are going to build a small chat application: we will be able to login, type messages and also have a screen with the message history.
It is not a trivial application: we will need a client side application, that runs in the browser, and a server side application, in our case a node application.
We are going to use a coding agent to make the architecture of the application and to write the actual code. For this tutorial we are going to use CODEX (OpenAI) as our LLM, but we could have used any other coding agent available.
We assume that you have installed the vmblu vscode extension and the vmblu runtime and cli (see quickstart if you haven't).
For this tutorial, we let the coding agent verify the files generated and test that code actually ran, but we did not deploy the formal vmblu test framework, because that would make this first example too long.
▞ Prompting
This is the prompt we used to get the process going:
# Introduction
- We are making a simple chat application. It consists of two sub-projects:
a chat server and a chat client.
# Steps to make the application
- In `./examples/chat-application`, initialize the projects:
- `vmblu init "chat-client"`
- `vmblu init "chat-server"`
- If `vmblu` is not on PATH, use:
`npx @vizualmodel/vmblu-cli init "<folder-name>"`
In order to develop these applications we are going to use the vmblu framework.
You can find the documents that you should read carefully before making the app
in `./chat-client/.vmblu/vmblu.prompt.md`. The same documents also exist in the
chat server, but you only have to read these once.
You will make the application in three steps:
1. first we will make the architecture in the respective model files:
`chat-client.mod.blu` and `chat-server.mod.blu`. Ask for approval after
having made the architecture.
2. When the architecture is approved, write the code for the nodes of the
application. In this phase your role is that of a developer or builder.
3. when the code for the nodes has been written, generate the test model
In this phase your role is that of a test engineer or verifier.
# Description of the application
## The chat client
- The chat client runs in the browser and uses svelte for its UI.
- The chat client should have a simple login window where the user just has
to type their name before starting the application.
- After login then there should be a window where the user sees the message
history, with their own messages in a green bubble to the right of the window,
and messages from other users in a blue bubble to the left of the window.
- Below the message history window there should be an input area where
the user can enter new messages.
## The chat server
- The chat server should keep track of the messages and when a user logs in,
send the message history to the client.
- The server should keep track of the users logged in
- The chat server is a Node.js application.
- Use ESM not CJS.With this prompt Codex started grinding and then came up with the following result:

and confirmed the work done :
CODEX 5.3
Step 1 (architecture) is complete and validated.
I initialized both projects and updated:
- `examples/chat-application/chat-client/chat-client.mod.blu`
- `examples/chat-application/chat-server/chat-server.mod.blu`
Architecture implemented:
- Client: `ui shell` (Svelte UI), `client controller`, `ws transport`
- Server: `ws gateway`, `chat state`
- Added explicit shared `types` (login/session, chat message, history,
delivery payloads)
- Wired connections for login flow, history-on-login, message send/receive,
and disconnect/session tracking
Validation run:
- `vmblu make-app chat-client.mod.blu` succeeded
- `vmblu make-app chat-server.mod.blu` succeeded
Please approve this architecture. After approval, I’ll proceed
to Step 2 and implement node code.The diagram looks the way it looks because the editor has placed the nodes and auto-routed the connections.
The architecture looks reasonable, but not granular enough: putting everything in a single ui-shell is not what we wanted, so we requested a bit more detail:
Ok nice work, but I think we need to split the ui shell into three different
nodes to make the architetcture clearer: a node for the login popup, one for
the message history and one for the message composer.The new diagram generated by the coding agent:

And the CODEX response:
CODEX 5.3
Refactor done in `examples/chat-application/chat-client/chat-client.mod.blu`.
Changes made:
- Replaced `ui shell` with three source nodes:
1. `login popup`
2. `message history`
3. `message composer`
- Updated `factories` to:
1. `./nodes/login-popup.js`
2. `./nodes/message-history.js`
3. `./nodes/message-composer.js`
4. `./nodes/client-controller.js`
5. `./nodes/ws-transport.js`
- Rewired connections:
1. `ui.show-login` -> `login popup.auth.show-login`
2. `login popup.auth.login-submitted` -> `client controller.auth.login-submitted`
3. `client controller.ui.show-room` -> both `message history.chat.show-room`
and `message composer.chat.show-room`
4. `client controller.ui.append-message` -> `message history.chat.append-message`
5. `message composer.chat.send-message` -> `client controller.chat.send-message`
6. `ui.connection-state` now fans out to both `message history` and
`message composer`
Validation:
- `make-app` succeeds for `chat-client.mod.blu` after removing stale derived
artifacts from the previous architecture (`chat-client.mod.viz`,
`chat-client.src.prf`).
If you approve this updated architecture, I’ll move to Step 2 and implement
the five client/server node code files.This looked already a lot more like what I was expecting. So I used that to do some minor re-layout work to get a more pleasing diagram:

The server layout was very simple, and I just left it as it was - more then adequate for this chat tutorial.
So at this stage I simply instructed CODEX to write the code. After some time it came back with the following message:
Step 2 is implemented: node code is written for both client and server.
Implemented files:
- `examples/chat-application/chat-client/nodes/login-popup.js`
- `examples/chat-application/chat-client/nodes/message-history.js`
- `examples/chat-application/chat-client/nodes/message-composer.js`
- `examples/chat-application/chat-client/nodes/client-controller.js`
- `examples/chat-application/chat-client/nodes/ws-transport.js`
- `examples/chat-application/chat-client/nodes/ui-state.js`
- `examples/chat-application/chat-server/nodes/ws-gateway.js`
- `examples/chat-application/chat-server/nodes/chat-state.js`
Package update:
- Added `ws` dependency in `examples/chat-application/chat-server/package.json`.
What this code does:
- Client:
1. Login popup collects username.
2. Message history renders left/right bubbles (others blue-left, own green-right).
3. Message composer emits outgoing text.
4. Controller handles flow between UI and transport.
5. WebSocket transport handles login/history/incoming/outgoing events and
connection state.
- Server:
1. WebSocket gateway accepts connections, receives login/messages, and
broadcasts outputs.
2. Chat state tracks logged-in users and message history, sends history on login,
and broadcasts new canonical messages.
Validation run:
- `vmblu profile chat-client.mod.blu` passed
- `vmblu profile chat-server.mod.blu` passed
- `vmblu make-app chat-client.mod.blu` passed
- `vmblu make-app chat-server.mod.blu` passed
- `node --check` passed for all new node files
Next natural step is Step 3 (test model + mirror node tests).Looked quite ok, but I wanted to have a look at the result first. Now, running the app required to start a server for the message service and also a server to get the client software to the browser. Iso doing this all myself, I let CODEX write a smaal script for that:
First I am going to run a test. Could write we a small bash script, run.sh,
that starts the server and the client and cleans up afterwards ?CODEX wrote a complete script in no time:
Added `examples/chat-application/run.sh`.
What it does:
1. Starts `chat-server` with `node chat-server.app.js`.
2. Starts `chat-client` with Vite on `http://127.0.0.1:5173`.
3. Installs dependencies in each project if `node_modules` is missing.
4. If missing, creates temporary `chat-client/index.html` and
`chat-client/main.js` so the generated app can load in browser.
5. Cleans up both processes (and temporary bootstrap files)
on `Ctrl+C` or exit.
Run it from repo root (or any location):
`bash examples/chat-application/run.sh`Running the application produced the following output:

The app worked, but some things CODEX had not done, and a number of improvements were in order:
Great job - it works quite well. Nevertheless a few remarks:
(1) you don't need main.js, put ./chat-client.app.js directly in index.html
(2) I don't think you used svelte as requested. I know this is a simple app,
but still I would like to use svelte components.
(3) As long as the user is not logged in, I just want to see the login popup,
nothing else. Now the message composer and history are inactive but
visible in the background.
(4) I also want to have a logout button so that I can logout and login as
another user to show multi-user support.Not all minor changes. Adding the logout button - and the consequences of that in the control of the app - turned out to be the biggest change.
Implemented all 4 requested changes.
What changed:
1. `index.html` now loads the app directly, no `main.js`
- Updated `examples/chat-application/chat-client/index.html`
- Removed `examples/chat-application/chat-client/main.js`
- Updated `examples/chat-application/run.sh` so bootstrap also points
directly to `./chat-client.app.js`
2. Switched UI to Svelte components
- Added Svelte UI components:
- `examples/chat-application/chat-client/nodes/ui/App.svelte`
- `examples/chat-application/chat-client/nodes/ui/LoginPopup.svelte`
- `examples/chat-application/chat-client/nodes/ui/MessageHistory.svelte`
- `examples/chat-application/chat-client/nodes/ui/MessageComposer.svelte`
- Added shared Svelte bridge/state:
- `examples/chat-application/chat-client/nodes/ui-bridge.js`
- Updated nodes to use the bridge:
- `examples/chat-application/chat-client/nodes/login-popup.js`
- `examples/chat-application/chat-client/nodes/message-history.js`
- `examples/chat-application/chat-client/nodes/message-composer.js`
- `examples/chat-application/chat-client/nodes/client-controller.js`
- `examples/chat-application/chat-client/nodes/ws-transport.js`
- Removed old DOM-based UI file:
- `examples/chat-application/chat-client/nodes/ui-state.js`
3. Before login, only login popup is visible
- `App.svelte` now conditionally renders:
- login screen only when logged out
- chat history/composer only when logged in
4. Added logout support (including relogin as another user)
- Extended architecture/pins in `/chat-client/chat-client.mod.blu`:
- `message history` outputs `auth.logout-request`
- `client controller` handles `auth.logout-request` and
outputs `auth.disconnect-request`
- `ws transport` handles `auth.disconnect-request`
- Controller now clears session/UI state and returns to login on
logout/disconnect.
- Transport now closes websocket on disconnect request.
Build/runtime updates:
- Added Svelte + Vite plugin support:
- `examples/chat-application/chat-client/vite.config.js`
- `examples/chat-application/chat-client/package.json` (Svelte 5 + plugin 6)
- Installed updated client deps and validated:
- `vmblu profile` and `vmblu make-app` (client) passed
- `npm.cmd run build` (client) passed
If you want, I can now also add a small user badge in the
chat header showing the current logged-in username.Nice work, but now I noticed that some of the code had been created in a separate folder as a sort of UI library. Not what we wanted: we wanted all code to be allocated to nodes.
Ok very nice job, but we have to do this differently: if a node has a ui
component,it should always be part of the node. The logic of the node and
the ui component should be stored in a folder under nodes that has the name
of the node, so I do not want to see this ui folder nor the ui-bridge.
The approach should be that a node maintains it ui and that he client
controller decides which ui has to be shown on the screen and where.
It can obtian the component (and any other necessary info) from the node
when it has to be shown, for example by using a request/reply.
All code in an app should be allocated to a node.Again, CODEX did as requested and updated the code and the architecture as required.
Refactor complete. The client now follows your required node-owned UI architecture.
What I changed:
1. UI is now owned by each UI node in its own folder (no shared `ui` folder,
no `ui-bridge`)
- `examples/chat-application/chat-client/nodes/login popup/index.js`
- `examples/chat-application/chat-client/nodes/login popup/LoginPopup.svelte`
- `examples/chat-application/chat-client/nodes/message history/index.js`
- `examples/chat-application/chat-client/nodes/message history/MessageHistory.svelte`
- `examples/chat-application/chat-client/nodes/message composer/index.js`
- `examples/chat-application/chat-client/nodes/message composer/MessageComposer.svelte`
Removed:
- `examples/chat-application/chat-client/nodes/ui-bridge.js`
- `examples/chat-application/chat-client/nodes/ui/` (entire folder)
- old flat UI node files (`login-popup.js`, `message-history.js`, `message-composer.js`)
2. Controller now decides what UI is shown and where, using request/reply
- Updated `examples/chat-application/chat-client/chat-client.mod.blu`:
- Added `ui.get-view` reply pins on each UI node.
- Added controller request pins:
- `ui.get-login-view`
- `ui.get-history-view`
- `ui.get-composer-view`
- Added `ui.hide-login` and `ui.current-user` outputs.
- Added `chat.current-user` input for message history.
- Rewrote `examples/chat-application/chat-client/nodes/client-controller.js`:
- Requests view elements from UI nodes.
- Mounts them into controller-owned slots.
- Controls login/chat visibility and connection/user state.
- Maintains logout flow.
3. Bootstrap still uses direct app script (no `main.js`)
- Restored/kept `examples/chat-application/chat-client/index.html` with:
- `./chat-client.app.js` directly
- `main.js` is not used.
Validation performed:
- `vmblu profile chat-client.mod.blu` passed
- `vmblu make-app chat-client.mod.blu` passed
- `npm.cmd run build` (chat-client) passed
If you want, I can now apply the same “node owns UI + controller mounts
via request/reply” pattern to any future UI nodes in this app as a template.The implementation was not bad, but I noticed that a lot of pins were used for actions that could be taken by the node itself.
Ok we have a good setup now, but I feel like we have a lot of messages related
to UI. Some of them are even misplaced ! In the node "login popup" the messages
auth.show-login and auth.hide-login should actually be "ui.show-login" and
"ui.hide-login". I think the requests to get the view should stay, but the
client-controller should be able to hide or show the view without sending
these explicit requests.Codex simplified the number of output/input pins.
Good call. I applied exactly that refactor.
What changed:
1. Corrected login popup pin naming
- In `examples/chat-application/chat-client/chat-client.mod.blu`:
- `auth.show-login` -> `ui.show-login`
- `auth.hide-login` -> `ui.hide-login`
2. Controller now controls visibility directly after `ui.get-view`
- In `examples/chat-application/chat-client/nodes/client-controller.js`:
- Removed sending `ui.show-login` / `ui.hide-login`
- Stores mounted view elements from `ui.get-*-view` replies
- Uses direct DOM visibility toggles (`setLoggedInLayout`, `setLoggedOutLayout`)
3. Login popup node no longer relies on show/hide message flow from controller
- In `examples/chat-application/chat-client/nodes/login popup/index.js`:
- Keeps `onUiShowLogin` / `onUiHideLogin` handlers (aligned naming)
- Visibility now primarily works with direct controller layout control
- In `examples/chat-application/chat-client/nodes/login popup/LoginPopup.svelte`:
- Removed store-driven visibility props; component renders normally in
its node-owned element
Validation:
- `vmblu profile chat-client.mod.blu` passed
- `vmblu make-app chat-client.mod.blu` passed
- `npm.cmd run build` passedAt this moment the design started to look quite good.

This architecture also allowed to look at all the data types, navigate the code, inspect comments etc.
It is also worthwhile to point out that, even with a simple application like this, there is some architectural complexity: the different ui widgets, the login/logout state, the message flow etc. An architecture overview brings this to the foreground iso being implicit and hidden in layers of code.
The code ran and had a better ui than at the start.

▞Conclusion
Yes, we know they tend to be sycophantic, but we wanted to know:
This is getting a clean design - good job. Did the vmblu framework help ?The answer is - as expected - positive, but the details give some insight into why the vmblu approach works well:
Yes, definitely.
Biggest wins from vmblu here:
1. Architecture-first iteration
- We could reshape node boundaries and message flows repeatedly
without losing track.
2. Explicit contracts
- Pin/type contracts made payload changes and refactors safer.
3. Runtime generation
- `make-app` gave immediate feedback on routing mistakes and stale
derived artifacts.
4. Profile visibility
- `profile` exposed handler/pin mapping issues quickly (and pushed us
to keep `tx.request` calls explicit).
5. Separation pressure
- The model made UI ownership and controller orchestration decisions
concrete, instead of implicit code coupling.
The main friction was keeping derived files (`.mod.viz`, generated app/profile)
in sync during rapid refactors, but the core architecture workflow was strong.Now the last 'friction' remark is a bit strange because the <model-name>.mod.viz file is a file the coding agent should ignore (as stated in the system prompt) - something to check and maybe improve the wording.
The coding agent followed the most formalised requests very strict, like the schemas to use for the files, Most of the other requests were also followed correctly, but some requests in the prompt files were 'overlooked' - to check if these requests can be 'tightened'. Maybe some later requests were in conflict with earlier ones...
In any case it was easy to guide the development and to have the coding agent correct the design or the code.
It is equally clear that having an explicit architecture helped a lot - both the human and the coding agent - and that making modifications or extensions to this app is going to be a lot easier.