Settings and themes✎
What is a project setting, what is yours, and how a colour scheme is written.
Verified
There are two kinds of setting and they behave differently, which is worth getting straight before changing either. Project settings are roswaal.json: committed, shared by everyone working on the repository, and they change what the compiler does. Preferences are yours — stored in your browser, never written to the project, and invisible to everybody else.
Info
| Page | Kept in | What is on it |
|---|---|---|
| Compiling | roswaal.json | Where graphs and Luau live, how a graph compiles, and indentation |
| Node packs | roswaal.json | The directories scanned for node packs |
| Canvas | This browser | The grid, wires, node corners, and what scrolling does |
| Nodes | This browser | Long names, and what a new logic, cast or concatenate node starts as |
| Workspace | This browser | Writing a graph, the Variables panel, function tabs, the action row, and what opens first |
| Themes | This browser | The colour scheme |
| Docs | This browser | The docs' font and the size of their pictures |
Search settings, at the top of the list, finds a setting on any page by its name or by what it does; Esc clears it, and Esc again closes Settings. Node Design has no project to change, so its Settings has no project pages; the docs' has only Themes and Docs, and says the rest is in the editor.
Project settings
Written to roswaal.json in the project root. roswaal init writes the defaults out; every one of them is optional in a hand-written file.
| Key | Default | What it does |
|---|---|---|
target | roblox | Which flavour of Luau new graphs compile for. lune is experimental, and not yet tested by an experienced Lune developer; it drops the Roblox globals and nodes. |
sourceDir | .roswaal/scripts | Where .nodescript and .nodemap files are read from. |
outDir | src | Where generated .luau is written. This is the directory Rojo syncs. |
compileMode | manual | hot recompiles a graph every time it is written, which is every edit; manual waits to be asked. The editor shows these as Dynamic and Manual — the stored name is hot for the sake of every roswaal.json already written. |
nodePaths | [".roswaal/nodes"] | Directories scanned for .nodedef.json node packs. |
format | true | Run stylua over generated files when it is on PATH. When it is not, the file is written unformatted rather than not written. |
comments | true | Write each comment's header into the generated Luau, above the code of the nodes it is drawn around. A header of one line is written -- like this, one of several as a --[[ ]] block. Off keeps them in the editor, which is what other visual scripting tools do — see Coming from Blueprints if that is the habit you have. |
castsByHierarchy | false | An Implicit Cast inside an Is A branch is always left out when the branch proved exactly its classes. On, it is also left out when the branch proved a class derived from the one it casts to — Is A Part covering a cast to BasePart. |
indentStyle | tab | What one level of indentation is in the generated Luau: tab, or space. Handed to stylua as well when format is on, so this decides rather than whatever stylua.toml says. |
indentWidth | 4 | How many spaces one level is, when indentStyle is space. Ignored otherwise, except that stylua is told it so a tab still counts the right amount against its column limit. |
rojoProject | unset | No longer read. An older roswaal.json may carry it, and it still loads. The Rojo project file is written by a node map, at the map's own Output — default.project.json unless it says otherwise — and only over a file Roswaal wrote, or one you hand over with force. |
place | unset | The project's place file, set by roswaal import. Unset, a .rbxl or .rbxlx in the project root, place.rbxl first. |
schemaVersion | set for you | Which schema the file was written against. migrate.ts reads it. |
Preferences
Stored in this browser under one key, and nowhere else. They do not follow you to another machine, which is the right thing to give up: the alternative is a per-developer file in a shared checkout.
| Preference | What it does |
|---|---|
| Theme | The colour scheme, or Follow the system — which is the absence of a theme rather than a scheme of its own, so the app keeps changing with your OS. |
| Wires | Curved is a bezier out of each pin, and the default. Rigid turns at right angles only. Angular leaves the pin level, takes one straight run to the other end, and arrives level — a diagonal rather than a cut corner. Where the input is behind the output there is no straight line to take, so angular borrows rigid's lane out and back, with its corners cut. |
| Node corners | Rounded or square. Capsule getters and reroute knots keep their shapes either way — a pill and a circle are what say this is a value and this is a bend in the wire, and neither has a title to say it instead. |
| Grid | The pattern behind a graph, on the canvas, in Node Design and on the start page. Dots, the default, puts a dot at every point Shift-drag snaps a node to, and leaves the nodes and wires the strongest thing on the canvas. Lines rules the canvas as Roswaal did before 0.150.0, every fifth line heavier, for anyone who lines nodes up by eye. Zoomed far out either keeps only every fifth. |
| Grid contrast | How strongly the grid draws. Auto, the default, is light, and strong when your system asks for more contrast. Light is faint, there when you look for it; Standard is easier to see on a bright screen; Strong is clearly visible, with larger dots, for low vision or a washed-out display. The grid only decorates: nothing about a graph is shown by it alone. |
| Long names | Truncate cuts a header too long for its node short, with the whole of it in the tooltip — what nodes have always done. Widen draws the node wide enough for its header instead. It is the one of these looks that moves pins, so the wire router and the pictures on these pages are computed from the same width: a node and its own picture are never two different sizes. |
| New concatenate nodes | What a new Concatenate writes: a join with .., or an interpolated string, where a part typed into the node is text and a part wired in is a hole in braces. The same string either way, and both are Luau, so the choice is about how the line reads. Stored on the node, as the brackets are — this only decides what one you drop today starts as, and Concatenation Type in the Inspector changes any of them. |
| New logic nodes | Whether a new And, Or, Not, comparison or arithmetic pill starts out bracketing its expression. Only the starting point: whether a node brackets is stored on the node, so it travels with the graph and reads the same on everybody's machine. Precedence is handled either way — this is about how the line reads, never about what it means. The casts are pills too and are not offered it: (value :: T) brackets itself already. |
| Shorten function tabs | What a function's tab says. None keeps ƒ hide (Occupancy); Function name and Script name keep one of the two. The tooltip has both. |
| Write a graph | How long after your last edit a graph is written. A delay, not a switch — there is no unsaved copy of a graph, so switching it off would give you a document that quietly stops matching itself rather than a buffer. |
| Exporting | What the Export menu starts with: zip or place, compile first, Modify RBXL, and the place's format. Kept in roswaal.json. |
| Modify DataModel Instances | Off by default. On, Properties changes values in the place file, after a backup. See Places and Rojo. |
| On opening Roswaal | Reopen the last project, or start at the picker. |
| Docs font | The face the docs are read in: System, Serif, Wide or Monospace. Code keeps its own. |
| Preview size | How large node and graph pictures are drawn in the docs, from 50% to 300%. A graph bigger than its frame can be dragged around. |
Info
Themes
One JSON file per scheme, in themes/ at the repository root, naming every colour the scheme sets.
| Scheme | Credit | Licence |
|---|---|---|
| Roswaal Light | neopolitans | 0BSD, with the repository |
| Roswaal Dark | neopolitans | 0BSD, with the repository |
| Tokyo Night | Enkia — MIT | MIT, in full under Settings → Licences |
| Tokyo Night Storm | Enkia — MIT | MIT, in full under Settings → Licences |
| Catppuccin Mocha | Catppuccin — MIT | MIT, in full under Settings → Licences |
| Nord | Sven Greb — MIT | MIT, in full under Settings → Licences |
| Aquatic | neopolitans | 0BSD, with the repository |
Adding one
Copy the closest scheme, rename it after the slug of its new name, and edit. Every field is required — there is deliberately no inheritance, because a half-defined palette silently borrowing another one's colours is far harder to debug than a missing-key error.
{
"name": "My Theme",
"order": 7,
"dark": true,
"credit": "You",
"colors": {
"app": "#16181d",
"panel": "#1b1e24",
"…": "…"
},
"code": {
"keyword": "#c98fd0",
"string": "#8fce9b",
"…": "…"
}
}npm run build:themes compiles themes/ into the bundle and refuses to build a scheme that would not work. What it checks is worth knowing before you hit it:
- Every role is present, and every colour is a full
#rrggbb. No short form, no names, no alpha. darkagrees with the actual brightness ofapp. It is not a description — every overlay is derived from it — so a scheme claimingtrueon a light ground would paint white onto white and make every hover state invisible.- A node is distinguishable from the canvas it sits on.
- Body text clears 4.5:1 against both
appandpanel, and the quieter text roles clear their own lower bars. - Syntax colours are visible on the surface code is shown on.
commentis held to a lower bar than the rest on purpose — every serious syntax theme mutes its comments, and that is the author's decision rather than a mistake to correct for them.
What a theme sets
| Role | What it colours |
|---|---|
app | The window behind everything |
panel | Sidebars, toolbars, dialogs |
canvas | The graph surface |
input | Text fields and dropdowns |
text | Body text |
subText | Labels and secondary lines |
dimText | Disabled and placeholder text |
border | Ordinary dividers |
borderStrong | Emphasised edges |
nodeBody | A node's fill |
nodeBorder | A node's outline |
wireExec | Execution wires |
accent | Primary buttons, focus, the active toggle |
danger | Errors and destructive actions |
warning | Warnings |
ok | Success, and a clean compile |
select | The selection outline on the canvas |
pure | A hint in the outline of a node with no side effects |
capsule | Inline chips and badges |
capsuleBorder | Their outline |
code.keyword | local, if, function |
code.string | String literals |
code.number | Number literals |
code.comment | Comments |
code.operator | Operators and punctuation |
code.property | Fields after a dot |
code.global | game, workspace, script |
code.function | Called names |
code.type | Types in annotations: x: Model |
What it does not
Node category colours and pin type colours are fixed. Red is a boolean, green is a number, gold is a vector — that mapping is most of what makes a Roswaal graph readable at a glance, and a scheme that moved it would be trading the one thing the colours are for against a matter of taste. They live in palette.ts and stay there.
Hover, the grid, the watermark and the node shadow are not authored either. They are overlays — a translucent white or black over whatever is underneath — and an overlay is the one kind of token an author gets wrong without seeing it, because the mistake is invisible on the surface they happened to be looking at. They are computed from dark instead, so a palette cannot ship a hover state that does not show.
Warning
Last reviewed 12 September 2026 by @neopolitans