Repository navigation
Advanced‐Mode
A node canvas for building the All-in-one string. Instead of typing a template, you drag blocks onto a surface and wire them together — the same way node editors work in Blender or Unreal.
Open it with the Advanced tab in the sidebar, or with the Advanced mode button at the top of the All in one card.
┌──────────────────────────────────────────────────────────────┐
│ ▸ Template 1 active │
│ Number of strings [2] ☐ Rotate strings every [25] sec │
│ AIO: [1•][2] 6 blocks, 8 connections Reset · Clear │
├────────────┬───────────────────────────────────┬─────────────┤
│ Blocks │ │ Block │
│ Variables │ canvas │ inspector │
│ │ │ │
└────────────┴───────────────────────────────────┴─────────────┘
| Area | Purpose |
|---|---|
| Template | The ten AIO templates, collapsed by default |
| Number of strings / Rotate | Same settings as the All in one card |
| AIO tabs | One canvas per string; a dot marks a canvas with content |
| Blocks | The palette, grouped; drag onto the canvas |
| Variables | Every placeholder, searchable; drops in as a block |
| Inspector | The selected block's fields |
Both side panels fold away with the button in their header, leaving a tab at the top edge to bring them back. The canvas takes the freed width.
| Action | Control |
|---|---|
| Add a block | Drag it from the palette |
| Connect | Drag from an output dot to an input dot |
| Disconnect | Drag away from a wired input |
| Move | Drag the block body |
| Pan | Middle mouse button |
| Zoom | Mouse wheel (0.35× – 2.4×) |
| Delete |
Del on the selection, or the inspector button |
An input holds one wire; connecting a second replaces the first. An output can feed as many inputs as you like.
Every canvas ends in a Chatbox Output block. A canvas without one is skipped by the rotation. A canvas with two is a mistake — only the first is used, and the status line says so.
| Block | Outputs | Notes |
|---|---|---|
| Text | Text | Fixed text; {placeholders} still work inside it |
| Placeholder | Value | Any placeholder by name, without the braces |
| Clock | Time |
strftime format, e.g. %H:%M
|
| Custom Box | Top line, Bottom line, Middle text | The frame, to place yourself |
| Personal Status | Text | Field: which template (active or 1–10) |
| Status text | Text | One specific slot; no rotation |
| GPU | Load, Temp, Watts, VRAM, Name | |
| CPU | Load, Temp, Watts, Name | |
| RAM & System | RAM, RAM %, RAM type, FPS | |
| MediaPlay | Artist, Title, Time, Songbar | |
| Chat / STT | Message | Field: chat / stt / ttt / any |
| Block | Inputs | Fields |
|---|---|---|
| Join | A–J (2–10, adjustable) | Separator; keep or skip empty inputs |
| Format | A, B, C | A pattern using {a} {b} {c}
|
| Info | Text, When, Otherwise | One page of a message — see below |
| Style | Text |
normal / super / sub / upper / lower
|
| Truncate | Text | Max characters, suffix (default …) |
| Line break | — | Outputs a newline |
Join grows and shrinks in the inspector. Wires are kept by socket, so growing it does not disturb what is already connected and the wire leaving it survives either way.
| Block | Inputs | Output |
|---|---|---|
| If / Else | Condition, Then, Else | Whichever branch applies |
| Compare | A, B | True when == != < > contains holds |
| Has value | Value | True when the input is not empty |
There is no separate boolean type. A value is true when it is a non-empty string, which is why a Compare can feed an If and a Has value can feed a Join without any conversion.
< and > compare as numbers when both sides look like numbers, and as
text otherwise — so 10 > 9 is true, which a string comparison would
get wrong.
| Block | Inputs | Fields |
|---|---|---|
| Timer | Start | Interval in seconds; blink or pulse
|
| Step | Advance, Reset | Number of steps (2–10); or every N seconds |
| Button | — |
pulse / toggle, plus a button in the inspector |
| Change AIO | When | Target: next / previous / 1–10 |
Timer modes:
-
blink— true for N seconds, then false for N seconds, repeating. -
pulse— true for one send every N seconds.
With nothing wired to Start it runs off the wall clock, so two timers with the same interval stay in step across a restart. Wire Start and it counts from the moment Start turns true and starts over when it turns false.
Step counts 1, 2, 3 … and wraps. Button is a manual trigger for
testing a chain without waiting for whatever would normally set it off.
| Block | Direction | Fields |
|---|---|---|
| Avatar parameter | in | Parameter name; outputs Value and True? |
| Set parameter | out | Name, type bool / int / float
|
| External OSC in | in | Any address; outputs Value, Type, True?, Text |
| External OSC out | out | Address, type, optional target IP and port |
Avatar parameter needs OSC input switched on under Options. A float is true above zero, so a radial works as a switch without a Compare in front of it.
Outgoing blocks fire when their When input is true, or — with nothing wired there — only when the value actually changed.
| Block | Purpose |
|---|---|
| Program running | True while a process whose name contains this runs |
| Start program | Launches a program on a rising edge |
| Get Hotkey | True while a key combination is held, anywhere |
| Send Hotkey | Presses a key combination at the operating system |
Program running matches a substring: vrchat finds VRChat.exe
under Proton. The list is polled every two seconds and shared between
blocks.
Start program has a folder button for picking an executable and a list of what is currently running. Debug runs the program in a terminal window that stays open after it exits — the difference between "it did not start" and "it started and said why it stopped". The command is split the way a shell would split it, but is not run through a shell.
Hotkeys need a backend: SendInput on Windows (built in), and
xdotool, wtype or ydotool on Linux. Get Hotkey additionally
needs python-evdev and read access to /dev/input, which usually
means being in the input group. The field records: press the round
button, then press the combination.
Set parameter, External OSC out, Send Hotkey, Change AIO and Start program never run while the preview repaints. A preview that silently flipped avatar toggles every time it redrew would be a trap.
Advanced mode ticks once per second on its own timer, independent of the chatbox send interval, so a Timer set to 2 seconds fires after 2 seconds. A message is only sent when it actually changed, so repeats do not spend the VRChat rate limit.
Every canvas gets to run its side-effect blocks on each tick, not just
the one currently on screen — a canvas holding nothing but
Button → Send Hotkey has no Chatbox Output at all and still works.
GPU ──(Load)──┐
├──► Join " | " ──► Chatbox Output
CPU ──(Load)──┘
MediaPlay ──(Title)──► Truncate 25 ──► Join
MediaPlay ──(Title)──► Has value ──┐
├──► If / Else ──► Chatbox Output
MediaPlay ──(Artist,Title)─► Join ─┤ Then
Text "nothing playing" ────────────┘ Else
Text "page two" ──► Info ──────────────► Chatbox Output
▲ When │
│ │ Shown?
GPU ─► Join ─► Info ─┘ Otherwise │
(no When wired = the fallback page) │
Timer blink 10s ◄─────────┘ Start
The Info block passes its Text through while it is the active page and hands over to Otherwise when it is not, so several of them chain into one output. The last one in the chain is the one with nothing wired to When — ungated means "show this when nobody else claims the turn".
For more than two pages, feed a Step into the When of several Info blocks and give each one a number in Show on step.
Chatbox Output ──(Shown?)──► Timer 10s pulse ──► Change AIO: next
Put the same chain on the other canvas and the two swap back and forth, with the timing living in the graph instead of in a setting.
Avatar parameter "MuteSelf" ──(True?)──► Send Hotkey ctrl+shift+m
Fires once per change, not once per send.
Program running "vrchat" ──┬──► Start program wlx-overlay-s
├──► Start program slimevr
└──► Start program mangohud --dlsym
Graphs are stored as ordinary JSON in config.json, under the active
template. Node positions are kept, so a canvas reopens exactly where you
left it. An unknown block type from a newer version costs that block,
not the file.
OSC-DreamChatbox · GPL-3.0-or-later · github.com/yakuda-stack/OSC-DreamChatbox