# Nova > A drawn GUI library for Matcha (Roblox): clean, modern windows with tabs, toggles, sliders, dropdowns, keybinds, color pickers and saved settings. Nova is a GUI library for Matcha, an external Luau executor for Roblox. Matcha can't create instances, so Nova draws windows with Matcha's Drawing library: tabs, toggles, sliders, dropdowns, text boxes, keybinds, color pickers, labels, images, notifications, and settings saved to JSON by flag. Load it at the top of a script (Matcha's loadstring drops return values, so the library also sets _G.Nova): ```lua local Nova = loadstring(game:HttpGet("https://raw.githubusercontent.com/Martinikaws/Nova/main/Nova.lua"))() or _G.Nova ``` These docs cover Nova and Matcha itself: the "Matcha" section lists what Matcha supports, what it doesn't, and the quirks that break scripts. Rules that matter when writing Nova code: - Make a window with Nova:CreateWindow({Title = ..., ConfigName = ...}), tabs with Window:Tab(name), and elements with Tab:Toggle / Slider / Dropdown / Textbox / Keybind / Colorpicker / Button / Label / Paragraph / Section / Divider / Image, each taking one options table. - Give an element a Flag to save it; read it anywhere with Nova.Flags[flag].Value, and change it with :Set(value). - Callbacks run inside the render step and are pcall'd; start a thread (task.spawn) for anything that waits. Button callbacks already run in their own thread. - Matcha has no mouse wheel: pages scroll by dragging or with Page Up / Page Down. Keys are given by name ("RightShift", "F", "F1") or Enum.KeyCode. - Clean up in the window's OnUnload option; Window:Destroy() removes everything. Rules that matter for any Matcha script: - There is no Instance.new, getgenv, hookfunction or request; share state through _G and fetch with game:HttpGet. - loadstring runs code but drops its return values. - Unsupported properties read as nil (no error); writing one errors. - Loops in spawned threads can stop resuming after the script ends; use RunService.Heartbeat / RenderStepped connections for ongoing work. - Input is polled: iskeypressed(vk), ismouse1pressed(), Player:GetMouse().X/Y; UserInputService.InputChanged doesn't exist (no mouse wheel). ## Introduction - [Getting Started](https://martinikaws.github.io/Nova/md/index.md): A drawn GUI library for Matcha. Build a clean, modern window with tabs, toggles, sliders, dropdowns and keybinds in a few lines. - [Example](https://martinikaws.github.io/Nova/md/start-example.md): See every element working: run the showcase, or read a short script that uses each one. ## Window - [CreateWindow](https://martinikaws.github.io/Nova/md/window-create.md): Makes a window and returns it. A script can make more than one. - [Window methods](https://martinikaws.github.io/Nova/md/window-methods.md): Everything a window can do after it's made. - [Notifications](https://martinikaws.github.io/Nova/md/window-notify.md): Short messages that slide in at the bottom right, even while the window is hidden. - [Saving & Flags](https://martinikaws.github.io/Nova/md/window-saving.md): Give an element a Flag and its value is saved and restored between runs. ## Elements - [Overview](https://martinikaws.github.io/Nova/md/elements.md): Every control you can put in a tab. Each one returns an object you can read and change later. - [Tabs & Sections](https://martinikaws.github.io/Nova/md/elements-tab.md): Tabs are the pages in the sidebar. Sections and dividers group the elements inside one. - [Button](https://martinikaws.github.io/Nova/md/elements-button.md): Runs a function when clicked. - [Toggle](https://martinikaws.github.io/Nova/md/elements-toggle.md): An on/off switch. - [Slider](https://martinikaws.github.io/Nova/md/elements-slider.md): Picks a number in a range by dragging. - [Dropdown](https://martinikaws.github.io/Nova/md/elements-dropdown.md): Picks one option, or several, from a list. - [Textbox](https://martinikaws.github.io/Nova/md/elements-textbox.md): Lets users type text. - [Keybind](https://martinikaws.github.io/Nova/md/elements-keybind.md): A key users can rebind, which runs your function when pressed. - [Colorpicker](https://martinikaws.github.io/Nova/md/elements-colorpicker.md): Picks a color with hue, saturation and brightness bars, swatches, or a hex code. - [Label & Paragraph](https://martinikaws.github.io/Nova/md/elements-label.md): Text that explains things. Both wrap to the page's width. - [Image](https://martinikaws.github.io/Nova/md/elements-image.md): Shows a picture, downloaded from a link or given as bytes. - [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md): Every element returns an object you can read and change later. ## Guides - [Key names](https://martinikaws.github.io/Nova/md/guides-keys.md): Keys can be given by name, key code, or Enum.KeyCode. - [How Nova handles Matcha](https://martinikaws.github.io/Nova/md/guides-matcha.md): What Nova does for you about Matcha's quirks, and its limits. For Matcha itself, see the Matcha section. - [Coming from Rayfield](https://martinikaws.github.io/Nova/md/guides-migrate.md): Most Rayfield-style scripts only need their window line changed. ## Matcha - [Matcha basics](https://martinikaws.github.io/Nova/md/matcha.md): Matcha is an external Luau VM for Roblox. It works differently from a normal executor, and scripts have to be written for that. - [Functions](https://martinikaws.github.io/Nova/md/matcha-functions.md): Every global function Matcha has, including the ones the official docs don't list yet. - [Classes & datatypes](https://martinikaws.github.io/Nova/md/matcha-classes.md): The Roblox classes, properties and types Matcha supports. Anything else reads as nil. - [Drawing](https://martinikaws.github.io/Nova/md/matcha-drawing.md): Matcha's only way to put things on screen. Everything Nova shows is made of these. ## Optional - [All docs in one file](https://martinikaws.github.io/Nova/llms-full.txt): every page above, in order - [Matcha's official docs](https://matcha-latte.gitbook.io/matcha/llms-full.txt): the Matcha API as its authors document it - [Library source](https://raw.githubusercontent.com/Martinikaws/Nova/main/Nova.lua): Nova.lua - [Example script](https://raw.githubusercontent.com/Martinikaws/Nova/main/example.lua): every element once # Full documentation --- # Getting Started > A drawn GUI library for Matcha. Build a clean, modern window with tabs, toggles, sliders, dropdowns and keybinds in a few lines. ## Install Load the library at the top of your script. There's nothing to download: it always runs the latest version from GitHub. ```lua local Nova = loadstring(game:HttpGet("https://raw.githubusercontent.com/Martinikaws/Nova/main/Nova.lua"))() or _G.Nova ``` > **Note:** Why `or _G.Nova`? Matcha's `loadstring` drops return values, so Nova also puts itself in `_G.Nova`. This line works in Matcha and in executors that do return. ## Your first window ```lua local Nova = loadstring(game:HttpGet("https://raw.githubusercontent.com/Martinikaws/Nova/main/Nova.lua"))() or _G.Nova local Window = Nova:CreateWindow({ Title = "My Script", Subtitle = "v1.0", ConfigName = "myscript", -- remembers every flagged value between runs }) local Main = Window:Tab("Main") Main:Section("General") Main:Toggle({ Name = "Enabled", Default = false, Flag = "Enabled", Callback = function(on) print("Enabled:", on) end, }) Main:Slider({Name = "Walk speed", Min = 16, Max = 100, Default = 16, Suffix = " studs", Flag = "Speed", Callback = function(v) print(v) end}) Main:Button({Name = "Hello", Callback = function() Window:Notify({Title = "Hello!", Content = "It works."}) end}) ``` Press `Right Shift` to show or hide the window. Drag it by the title. Every window also gets a **Settings** tab, where users can change the show/hide key and the accent color and unload the script. ## Next steps - [Window](https://martinikaws.github.io/Nova/md/window-create.md): Title, size, accent, keys and saving. - [Elements](https://martinikaws.github.io/Nova/md/elements-toggle.md): Every control you can put in a tab. - [Matcha](https://martinikaws.github.io/Nova/md/matcha.md): What Matcha supports, and the quirks that break scripts. - [Full example](https://martinikaws.github.io/Nova/md/start-example.md): Every element in one script. --- # Example > See every element working: run the showcase, or read a short script that uses each one. ## Showcase One line in Matcha opens a window that demonstrates everything: every element, the methods that change them from code, the window's own methods, labels and images, and scrolling. What you change shows up live, and the values are saved between runs. ```lua loadstring(game:HttpGet("https://raw.githubusercontent.com/Martinikaws/Nova/main/showcase.lua"))() ``` > **Note:** Right Shift shows and hides it. Its source is [showcase.lua](https://github.com/Martinikaws/Nova/blob/main/showcase.lua), a good place to copy from. ## A short example ```lua local Nova = loadstring(game:HttpGet("https://raw.githubusercontent.com/Martinikaws/Nova/main/Nova.lua"))() or _G.Nova local Window = Nova:CreateWindow({Title = "Nova", Subtitle = "Example", ConfigName = "example"}) local Main = Window:Tab("Main") Main:Section("Basics") Main:Label("Everything here is drawn with Matcha's Drawing library.") Main:Toggle({Name = "Enabled", Default = false, Flag = "Enabled", Callback = function(on) print("Enabled:", on) end}) Main:Slider({Name = "Walk speed", Min = 16, Max = 100, Default = 16, Suffix = " studs", Flag = "Speed", Callback = function(v) print("Speed:", v) end}) Main:Button({Name = "Say hi", Callback = function() Window:Notify({Title = "Hi!", Content = "Buttons run their callback when clicked.", Duration = 3}) end}) Main:Section("Choices") Main:Dropdown({Name = "Mode", Options = {"Legit", "Balanced", "Chaos"}, Default = "Balanced", Flag = "Mode", Callback = function(opt) print("Mode:", opt) end}) Main:Dropdown({Name = "Parts", Options = {"Head", "Torso", "Arms", "Legs"}, Multi = true, Default = {"Head"}, Flag = "Parts", Callback = function(list) print(table.concat(list, ", ")) end}) Main:Textbox({Name = "Player name", Placeholder = "Type and press Enter", Flag = "Target", Callback = function(text) print("Target:", text) end}) local Extra = Window:Tab("Extra") Extra:Keybind({Name = "Say something", Default = "G", Flag = "SayKey", Callback = function() Window:Notify({Title = "Keybind", Content = "You pressed the key."}) end}) Extra:Keybind({Name = "Hold to show", Default = "H", Mode = "Hold", Callback = function(held) print(held) end}) Extra:Colorpicker({Name = "Box color", Default = Color3.fromRGB(255, 80, 80), Flag = "BoxColor", Callback = function(c) print(c) end}) Extra:Paragraph({Title = "About", Content = "Long tabs scroll: drag the page or its bar, or use Page Up / Page Down."}) print("Speed is", Nova.Flags.Speed.Value) ``` The same file is in the repository as [example.lua](https://github.com/Martinikaws/Nova/blob/main/example.lua). --- # CreateWindow > Makes a window and returns it. A script can make more than one. ```lua Nova:CreateWindow(options: table) -> Window ``` ```lua local Window = Nova:CreateWindow({ Title = "My Script", Subtitle = "v1.0", ConfigName = "myscript", ToggleKey = "RightShift", Accent = "eab308", Size = {620, 460}, }) ``` ## Options | Option | Type | Default | Description | | --- | --- | --- | --- | | `Title` | string | `"Nova"` | Shown at the top of the sidebar. | | `Subtitle` | string | none | A smaller line under the title, in the accent color. | | `ConfigName` | string | none | Saves flagged elements to `Folder/ConfigName.json`. Without it nothing is saved. See [Saving](https://martinikaws.github.io/Nova/md/window-saving.md). | | `Folder` | string | `"Nova"` | The workspace folder settings are saved in. | | `ToggleKey` | key | `"RightShift"` | The key that shows and hides the window. See [Key names](https://martinikaws.github.io/Nova/md/guides-keys.md). | | `Accent` | hex / Color3 | `"eab308"` (yellow) | The highlight color. Users can change it in Settings. | | `Size` | {w, h} | `{620, 460}` | Window size in pixels (at least 460 × 320). It shrinks to fit small screens. | | `Open` | boolean | `true` | Start shown. With `false`, the window waits for the toggle key. | | `Settings` | boolean | `true` | Add the built-in Settings tab (show/hide key, accent, mouse offset, reset, unload). | | `MouseOffset` | number | `0` | Pixels added to the mouse's y. Users can adjust it in Settings if clicks land off. | | `Theme` | table | none | Override colors: `bg`, `side`, `panel`, `card`, `cardHover`, `line`, `text`, `dim`, `faint` (hex strings or Color3). | | `OnUnload` | function | none | Runs when the window is destroyed (from code or Settings → Unload). Clean up your connections here. | > **Note:** The window starts centered. Users can drag it by the title area, and it remembers where it was until the script ends. --- # Window methods > Everything a window can do after it's made. | Method | Description | | --- | --- | | `Window:Tab(name) -> Tab` | Adds a tab to the sidebar. Aliases: `CreateTab`, `AddTab`. | | `Window:SelectTab(tab \| name)` | Switches to a tab. | | `Window:Notify(options)` | Shows a notification. See [Notifications](https://martinikaws.github.io/Nova/md/window-notify.md). | | `Window:Toggle(open?)` | Shows or hides the window. Without an argument, it flips. | | `Window:IsOpen() -> boolean` | Whether the window is shown. | | `Window:SetAccent(color)` | Changes the accent (hex string or Color3). | | `Window:GetAccent() -> Color3` | The current accent. | | `Window:SetToggleKey(key)` | Changes the show/hide key. | | `Window:GetToggleKey() -> string` | The show/hide key's name, like `"RightShift"`. | | `Window:SetTitle(title, subtitle?)` | Changes the title (and subtitle). | | `Window:Save()` | Saves flags right away (normally a second after the last change). | | `Window:Destroy()` | Removes the window and everything it drew, and gives input back to the game. Alias: `Unload`. | | `Window:Dump() -> string` | Every text on screen with its position. Handy for checking a layout from a script. | ## Fields | Field | Description | | --- | --- | | `Window.Flags` | This window's flagged elements, by flag. | | `Window.Tabs` | The tabs, in order. | | `Window.SettingsTab` | The built-in Settings tab. Add your own elements to it if you like. | ## Library functions | Function | Description | | --- | --- | | `Nova:CreateWindow(options)` | Makes a window. Alias: `Nova:Window`. | | `Nova:Notify(options)` | A notification on the newest window. | | `Nova:Destroy()` | Destroys every window. | | `Nova.Flags` | Every flagged element from every window. | | `Nova.Hex(hex) -> Color3` | `"ff5050"` to a Color3. | | `Nova.KeyLabel(key) -> string` | How a key is shown, like `"Right Shift"`. | | `Nova.Version` | The library version. | --- # Notifications > Short messages that slide in at the bottom right, even while the window is hidden. ```lua Window:Notify({Title, Content?, Duration?}) ``` ```lua Window:Notify({ Title = "Saved", Content = "Your settings were saved.", Duration = 4, -- seconds }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Title` | string | `"Notice"` | The bold first line. | | `Content` | string | none | More text. It wraps. | | `Duration` | number | `4` | Seconds before it fades out. A thin bar shows the time left. | Up to five show at once; the oldest goes first. `Window:Notify("Text")` works too, using the text as the title. --- # Saving & Flags > Give an element a Flag and its value is saved and restored between runs. ```lua local Window = Nova:CreateWindow({Title = "My Script", ConfigName = "myscript"}) local Main = Window:Tab("Main") Main:Toggle({Name = "Enabled", Flag = "Enabled", Callback = function(on) ... end}) -- anywhere later: if Nova.Flags.Enabled.Value then ... end ``` ## How it works - With a `ConfigName`, flagged values are written to `Nova/.json` in Matcha's workspace, a second after the last change and when the window is destroyed. - When the script runs again, each flagged element starts from its saved value instead of its `Default`. - For a restored value, the element's `Callback` runs once on the first frame, so your script applies it just like a click would. Keybinds are the exception: they only fire on a key press. - The show/hide key, the accent and the mouse offset from the Settings tab are saved too. ## Reading flags `Nova.Flags[flag]` (and `Window.Flags[flag]`) is the element itself, so you can read `.Value` or call `:Set()` from anywhere. ```lua Nova.Flags.Speed:Set(50) -- moves the slider and runs its callback print(Nova.Flags.Mode.Value) -- "Balanced" ``` > **Note:** Flags must be unique across windows. Use a prefix like `"MyScript_Speed"` if several scripts share a session. ## What gets saved | Element | Saved as | | --- | --- | | Toggle | true / false | | Slider | number | | Dropdown | string, or a list for Multi | | Textbox | string | | Keybind | key name, like `"G"` or `"None"` | | Colorpicker | `{"color": "ff5050"}` | --- # Overview > Every control you can put in a tab. Each one returns an object you can read and change later. - [Toggle](https://martinikaws.github.io/Nova/md/elements-toggle.md): On/off switch - [Slider](https://martinikaws.github.io/Nova/md/elements-slider.md): A number in a range - [Button](https://martinikaws.github.io/Nova/md/elements-button.md): Runs a function - [Dropdown](https://martinikaws.github.io/Nova/md/elements-dropdown.md): One or many options - [Textbox](https://martinikaws.github.io/Nova/md/elements-textbox.md): Typed text - [Keybind](https://martinikaws.github.io/Nova/md/elements-keybind.md): Rebindable keys - [Colorpicker](https://martinikaws.github.io/Nova/md/elements-colorpicker.md): H, S, V, swatches, hex - [Tabs & Sections](https://martinikaws.github.io/Nova/md/elements-tab.md): Pages and groups - [Label & Paragraph](https://martinikaws.github.io/Nova/md/elements-label.md): Wrapped text - [Image](https://martinikaws.github.io/Nova/md/elements-image.md): From a link or bytes - [Notifications](https://martinikaws.github.io/Nova/md/window-notify.md): Slide-in messages - [Saving & Flags](https://martinikaws.github.io/Nova/md/window-saving.md): Remembered between runs All elements share the same methods: [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). --- # Tabs & Sections > Tabs are the pages in the sidebar. Sections and dividers group the elements inside one. ```lua Window:Tab(name: string) -> Tab ``` ```lua local Main = Window:Tab("Main") Main:Section("Movement") Main:Toggle({Name = "Fly"}) Main:Divider() Main:Toggle({Name = "Noclip"}) ``` Elements stack from top to bottom in the order you add them. When a tab gets longer than the window, it scrolls: drag the page, drag its scrollbar, or use `Page Up` / `Page Down` and the arrow keys while pointing at it. ## Section ```lua Tab:Section(name: string) ``` A small accent heading with a line, to split a tab into groups. ## Divider ```lua Tab:Divider() ``` A thin line with no text. > **Note:** The Settings tab always stays last, even for tabs you add after the window is made. --- # Button > Runs a function when clicked. ```lua Tab:Button({Name, Callback}) ``` ```lua Main:Button({ Name = "Teleport to spawn", Callback = function() print("clicked") end, }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Callback` | function() | none | Runs on click, in its own thread, so it can wait. | Returns the element. Keep it to change it later: `:Set(value)`, `:Get()`, `.Value`; see [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). --- # Toggle > An on/off switch. ```lua Tab:Toggle({Name, Default?, Flag?, Callback?}) -> Element ``` ```lua local Esp = Main:Toggle({ Name = "Show names", Default = false, Flag = "ShowNames", Callback = function(on) print("Show names:", on) end, }) Esp:Set(true) -- turn it on from code print(Esp.Value) -- true ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Default` | boolean | `false` | The starting state. | | `Flag` | string | none | Saves the state. See [Saving](https://martinikaws.github.io/Nova/md/window-saving.md). | | `Callback` | function(on) | none | Runs with the new state when it changes. | Returns the element. Keep it to change it later: `:Set(value)`, `:Get()`, `.Value`; see [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). --- # Slider > Picks a number in a range by dragging. ```lua Tab:Slider({Name, Min, Max, Default?, Increment?, Suffix?, Flag?, Callback?}) -> Element ``` ```lua Main:Slider({ Name = "Field of view", Min = 70, Max = 120, Default = 90, Increment = 1, Suffix = "°", Flag = "FOV", Callback = function(v) print("FOV:", v) end, }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Min` | number | `0` | The lowest value. `Range = {min, max}` works too. | | `Max` | number | `100` | The highest value. | | `Default` | number | `Min` | The starting value. | | `Increment` | number | `1` | Step size. Use `0.1` or `0.01` for decimals. | | `Suffix` | string | none | Shown after the value, like `" studs"` or `"%"`. | | `Flag` | string | none | Saves the value. | | `Callback` | function(value) | none | Runs with each new value while dragging. | Returns the element. Keep it to change it later: `:Set(value)`, `:Get()`, `.Value`; see [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). > **Note:** `:Set(v)` clamps to the range and rounds to the increment. --- # Dropdown > Picks one option, or several, from a list. ```lua Tab:Dropdown({Name, Options, Default?, Multi?, Flag?, Callback?}) -> Element ``` ```lua local Mode = Main:Dropdown({ Name = "Mode", Options = {"Legit", "Balanced", "Chaos"}, Default = "Balanced", Flag = "Mode", Callback = function(option) print("Mode:", option) end, }) -- pick several Main:Dropdown({ Name = "Parts", Options = {"Head", "Torso", "Arms", "Legs"}, Multi = true, Default = {"Head"}, Callback = function(list) print(table.concat(list, ", ")) end, }) -- new options later (for example a player list) Mode:Refresh({"A", "B", "C"}) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Options` | {string} | `{}` | The choices. | | `Default` | string / {string} | none | The starting choice (a list with Multi). | | `Multi` | boolean | `false` | Let users pick several. The value becomes a list. | | `Flag` | string | none | Saves the choice. | | `Callback` | function(value) | none | Runs with the choice (or the list) when it changes. | ## Refresh ```lua Dropdown:Refresh(options: {string}, keep: boolean?) ``` Replaces the options. Unless `keep` is true, the value resets to the first option (or an empty list with Multi). > **Note:** The list opens under the box (above it near the bottom of the window) and shows seven rows at a time. Drag it or use the arrow keys to scroll. Clicking outside closes it. --- # Textbox > Lets users type text. ```lua Tab:Textbox({Name, Placeholder?, Default?, Numeric?, ClearOnFocusLost?, Flag?, Callback?}) -> Element ``` ```lua Main:Textbox({ Name = "Player name", Placeholder = "Type and press Enter", Flag = "Target", Callback = function(text) print("Target:", text) end, }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Placeholder` | string | `""` | Grey text while it's empty. | | `Default` | string | `""` | The starting text. | | `Numeric` | boolean | `false` | Only keep a number (anything else becomes empty). | | `ClearOnFocusLost` | boolean | `false` | Empty the box after each entry, for command-style input. | | `Flag` | string | none | Saves the text. | | `Callback` | function(text) | none | Runs when typing ends: `Enter`, `Esc`, or clicking somewhere else. | Returns the element. Keep it to change it later: `:Set(value)`, `:Get()`, `.Value`; see [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). > **Note:** While a box is focused, typing goes to it and not to the game, and the show/hide key doesn't work. Letters, digits, space and common symbols are supported, with `Shift` for capitals; hold `Backspace` to delete quickly. --- # Keybind > A key users can rebind, which runs your function when pressed. ```lua Tab:Keybind({Name, Default?, Mode?, Flag?, Callback?, Changed?}) -> Element ``` ```lua Main:Keybind({ Name = "Toggle fly", Default = "F", Mode = "Toggle", Flag = "FlyKey", Callback = function(state) print("Fly:", state) end, }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Default` | key | none | The starting key, like `"F"` or `"LeftAlt"`. See [Key names](https://martinikaws.github.io/Nova/md/guides-keys.md). | | `Mode` | string | `"Press"` | `"Press"`: runs on each press. `"Toggle"`: flips a state on each press and passes it. `"Hold"`: passes `true` on press and `false` on release. | | `Flag` | string | none | Saves the key. | | `Callback` | function(state?) | none | Runs on the key, as described in Mode. | | `Changed` | function(keyName) | none | Runs when the user rebinds it, with the new key's name (or `"None"`). | ## Rebinding Click the key chip, then press a key. `Esc` cancels and `Backspace` clears it to None. Keybinds work while the window is hidden, but not while typing in a text box. Returns the element. Keep it to change it later: `:Set(value)`, `:Get()`, `.Value`; see [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). --- # Colorpicker > Picks a color with hue, saturation and brightness bars, swatches, or a hex code. ```lua Tab:Colorpicker({Name, Default?, Flag?, Callback?}) -> Element ``` ```lua Main:Colorpicker({ Name = "Box color", Default = Color3.fromRGB(255, 80, 80), -- or "ff5050" Flag = "BoxColor", Callback = function(color) print(color) end, }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Name` | string | none | The label. | | `Default` | Color3 / hex | the accent | The starting color. | | `Flag` | string | none | Saves the color. | | `Callback` | function(Color3) | none | Runs with each new color while picking. | Clicking the swatch opens the picker: drag the **H**, **S** and **V** bars, click one of the ten swatches, or click the hex box and type a code like `ff5050` (then `Enter`). Alias: `ColorPicker`. Returns the element. Keep it to change it later: `:Set(value)`, `:Get()`, `.Value`; see [Element methods](https://martinikaws.github.io/Nova/md/elements-methods.md). --- # Label & Paragraph > Text that explains things. Both wrap to the page's width. ## Label ```lua Tab:Label(text | {Text, Color?}) -> Element ``` ```lua local Status = Main:Label("Waiting...") Status:SetText("Ready!") Main:Label({Text = "Something went wrong", Color = "f05a5a"}) ``` ## Paragraph ```lua Tab:Paragraph({Title, Content}) -> Element ``` ```lua local Info = Main:Paragraph({ Title = "How to use", Content = "Turn on Enabled, then pick a mode. Your choices are saved.", }) Info:Set("New content") -- content only Info:Set("New title", "New content") -- both ``` --- # Image > Shows a picture, downloaded from a link or given as bytes. ```lua Tab:Image({Url | Data, Height?, Width?}) -> Element ``` ```lua Main:Image({ Url = "https://example.com/banner.png", Height = 120, }) ``` | Option | Type | Default | Description | | --- | --- | --- | --- | | `Url` | string | none | Downloaded with `game:HttpGet` when the element is made. It shows "Loading..." until the download finishes. | | `Data` | string | none | The image's bytes, if you already have them (for example from readfile). | | `Height` | number | `120` | Height in pixels. | | `Width` | number | page width | Width in pixels, centered. | > **Warning:** Use PNG or JPG. Matcha's `writefile` refuses the .png extension, so if you cache images in the workspace, save them with another extension like .dat. --- # Element methods > Every element returns an object you can read and change later. | Method / field | Description | | --- | --- | | `el.Value` | The current value (state, number, option, text, key code or Color3). | | `el:Set(value, silent?)` | Changes the value and runs the callback (unless `silent` is true). Aliases: `SetValue`. | | `el:Get()` | The value. Alias: `GetValue`. | | `el:SetName(text)` | Changes the label. | | `el:SetVisible(bool)` | Hides or shows the element. The elements below move up. | | `el:SetText(text)` | Labels only: changes the text. | | `el:Refresh(options, keep?)` | Dropdowns only: new options. | | `el.Name / el.Flag / el.Kind` | The label, the flag, and the kind (`"Toggle"`, `"Slider"`, ...). | ## Aliases If you're coming from another library, the familiar names work too: | Nova | Also works | | --- | --- | | `Tab:Toggle` | `AddToggle`, `CreateToggle` | | `Tab:Textbox` | `Input`, `AddInput`, `CreateInput`, `AddTextbox` | | `Tab:Colorpicker` | `ColorPicker`, `AddColorPicker`, `CreateColorPicker` | | `(every element)` | `Add` and `Create` | | `Slider options` | `Range = {min, max}`, `Rounding` for Increment, `CurrentValue` for Default | | `Dropdown options` | `Values` for Options, `MultipleOptions` for Multi, `CurrentOption` for Default | --- # Key names > Keys can be given by name, key code, or Enum.KeyCode. ```lua ToggleKey = "RightShift" -- name ToggleKey = Enum.KeyCode.Insert -- Enum.KeyCode ToggleKey = 0x2D -- Windows key code ``` | Keys | Names | | --- | --- | | Letters | `"A"` to `"Z"` | | Numbers | `"One"` ... `"Nine"`, `"Zero"` (or `"1"` ... `"0"`); keypad: `"KeypadOne"` ... | | Function keys | `"F1"` to `"F12"` | | Modifiers | `"LeftShift"`, `"RightShift"`, `"LeftControl"`, `"RightControl"`, `"LeftAlt"`, `"RightAlt"` | | Navigation | `"Insert"`, `"Delete"`, `"Home"`, `"End"`, `"PageUp"`, `"PageDown"`, `"Up"`, `"Down"`, `"Left"`, `"Right"` | | Other | `"Space"`, `"Tab"`, `"Return"`, `"Backspace"`, `"CapsLock"`, `"Escape"`, `"Minus"`, `"Equals"`, `"Comma"`, `"Period"`, `"Slash"`, `"Semicolon"`, `"Quote"`, `"LeftBracket"`, `"RightBracket"`, `"BackSlash"`, `"Backquote"` | | None | `"None"`, `nil` or `false` | --- # How Nova handles Matcha > What Nova does for you about Matcha's quirks, and its limits. For Matcha itself, see the Matcha section. ## Everything is drawn Matcha can't create instances, so there are no ScreenGuis. Every window is made of Drawing objects: squares, text, lines, circles and images. They're redrawn every frame from a pool, and a property is only written when it changes, so an idle window costs almost nothing. ## Input - **No mouse wheel.** Matcha can't read it. Pages scroll by dragging them, dragging the scrollbar, or with `Page Up` / `Page Down` and the arrow keys while pointing at them. - **Clicks count on release.** So pressing and dragging scrolls instead of clicking whatever you started on. - **The game doesn't get your clicks.** While the mouse is over the window, or you're typing or binding a key, Nova calls `setrobloxinput(false)`, and gives input back afterwards. - **Clicks land off?** Some setups report the mouse a few pixels away from where it's drawn. Users can fix it in **Settings → Mouse offset**, or you can set `MouseOffset`. The value is saved. - **Roblox must be focused.** Keys and clicks are read with `iskeypressed` and `ismouse1pressed`. They're ignored while another window is in front. ## Callbacks The window runs from a `RenderStepped` connection. That keeps it alive after your script finishes (in Matcha, a loop in a spawned thread can stop resuming). Callbacks run inside that step and are wrapped in `pcall`: an error is printed and the window keeps going. > **Note:** Keep callbacks short. For anything that waits or loops, start your own thread: `Callback = function(on) task.spawn(doLongThing, on) end`. Button callbacks already get their own thread. ## Text Matcha draws text a little higher than its position. Nova shifts it back, and measures text with `TextBounds` so long labels get trimmed with ".." instead of overflowing. ## Unloading `Window:Destroy()` (or **Settings → Unload**) removes every drawing, disconnects the render step, gives input back to the game, saves flags and calls your `OnUnload`. Disconnect your own connections there. ```lua local conn = game:GetService("RunService").Heartbeat:Connect(function() ... end) local Window = Nova:CreateWindow({ Title = "My Script", OnUnload = function() conn:Disconnect() end, }) ``` --- # Coming from Rayfield > Most Rayfield-style scripts only need their window line changed. ```lua -- Rayfield local Window = Rayfield:CreateWindow({Name = "My Script"}) local Tab = Window:CreateTab("Main") Tab:CreateToggle({Name = "Fly", CurrentValue = false, Flag = "Fly", Callback = function(v) end}) Tab:CreateSlider({Name = "Speed", Range = {16, 100}, Increment = 1, CurrentValue = 16, Callback = function(v) end}) -- Nova: same element calls, a different window line local Window = Nova:CreateWindow({Title = "My Script"}) local Tab = Window:CreateTab("Main") Tab:CreateToggle({Name = "Fly", CurrentValue = false, Flag = "Fly", Callback = function(v) end}) Tab:CreateSlider({Name = "Speed", Range = {16, 100}, Increment = 1, CurrentValue = 16, Callback = function(v) end}) ``` | Rayfield | Nova | | --- | --- | | `CreateWindow({Name})` | `CreateWindow({Title})` | | `ConfigurationSaving = {Enabled, FileName}` | `ConfigName` | | `Rayfield:Notify({Title, Content, Duration})` | `Window:Notify` or `Nova:Notify` | | `Flags[flag].CurrentValue` | `Flags[flag].Value` | | `Element:Set(v)` | `Element:Set(v)` (same) | > **Warning:** Icons, key systems and Discord invites aren't part of Nova. --- # Matcha basics > Matcha is an external Luau VM for Roblox. It works differently from a normal executor, and scripts have to be written for that. ## What Matcha is Matcha runs **outside** the Roblox client. It doesn't hook the engine; it reads and writes the game's memory and emulates the Roblox API on top of that. So it only has the classes, properties and functions it implements. The [official docs](https://matcha-latte.gitbook.io/matcha) list them, and the pages here add what we found in practice. ## Rules that break scripts - **Bindings are a fixed list.** Reading a property Matcha doesn't support doesn't error: it falls back to `FindFirstChild` and gives `nil`. An unexplained `nil` usually means "unsupported". **Writing** an unsupported property errors (`Unknown property`). - **No instances.** There is no `Instance.new`, so no ScreenGuis, parts or sounds of your own. Draw UI with the [Drawing library](https://martinikaws.github.io/Nova/md/matcha-drawing.md) (that's what Nova does). - **`loadstring` drops return values.** `loadstring("return 5")()` gives nothing, even through `pcall`. Share values through `_G` instead; there is no `getgenv` or `shared`. That's why Nova loads with `... or _G.Nova`. - **Threads can stop.** A `task.spawn` loop that waits with `task.wait` can stop resuming once the script that started it has finished. For anything that must keep running, connect to `RunService.Heartbeat` or `RenderStepped` and throttle inside it. - **No mouse wheel.** `UserInputService.InputChanged` is `nil`; only `InputBegan` and `InputEnded` exist. Poll input instead: `iskeypressed`, `ismouse1pressed` and `Player:GetMouse()` (`Mouse.X` / `Mouse.Y`). - **Roblox must be focused** for key and mouse input, including `keypress` and `mouse1click`. Check `isrbxactive()`. - **Your clicks also reach the game.** Call `setrobloxinput(false)` while the mouse is over your UI, and `setrobloxinput(true)` afterwards. - **Autoexec runs before the game exists.** `game.GameId` is `0` until it loads, and yielding at the top level of an autoexec script errors. Wait inside `task.spawn` for `game:IsLoaded()` and `LocalPlayer`, then check `game.GameId` before doing anything game-specific. - **Files:** `writefile` refuses some extensions (`.png` among them). Save binary data with another extension such as `.dat`. - **Memory functions** need unsafe Luau execution enabled in Matcha. A bad `memory_write` can crash the game. ## Patterns that work ```lua -- only in one game, and only once it has loaded (safe in autoexec) local GAME_ID = 6035872082 task.spawn(function() while not (game:IsLoaded() and game:GetService("Players").LocalPlayer) do task.wait(0.5) end if game.GameId ~= GAME_ID then return end -- ... end) -- something that keeps running: a connection, throttled local last = 0 local conn = game:GetService("RunService").Heartbeat:Connect(function() if tick() - last < 0.5 then return end last = tick() -- ... end) -- a value another script can reach (loadstring can't return it) _G.MyScript = {stop = function() conn:Disconnect() end} ``` > **Note:** The Matcha-specific files: [official docs as one text file](https://matcha-latte.gitbook.io/matcha/llms-full.txt). Everything on these pages is also in Nova's [llms.txt](llms.txt). --- # Functions > Every global function Matcha has, including the ones the official docs don't list yet. ## Scripts and globals | Function | Description | | --- | --- | | `loadstring(source, chunkname?) -> function?, error?` | Compiles code. The function it gives back runs, but its return values are lost (see Matcha basics). | | `require(path) -> table` | Runs a workspace file ending in .lua or .luau. | | `decompile(script) -> string` | Decompiles a LocalScript or ModuleScript. | | `getscripts() -> {Instance}` | All scripts in the game. | | `getscriptbytecode(script) -> string` | A script's bytecode. | | `getscripthash(script) -> string` | A hash of a script's bytecode, to tell versions apart. | | `getgc() / findgc(...)` | Garbage-collector access. Not documented; findgc is slow (tens of seconds). | | `identifyexecutor() -> string` | The executor name and version, like "Matcha 1.0.0". | | `notify(message, title, seconds)` | A Matcha notification. | | `WorldToScreen(position) -> Vector2, boolean` | A 3D point on screen, and whether it's visible. | | `base64encode(s) / base64decode(s)` | Base64 conversion. | | `setfflag(name, value) / getfflag(name)` | Fast flags (unfinished in Matcha). | | `print / printl / warn / error` | Console output. | ## Input | Function | Description | | --- | --- | | `iskeypressed(keycode) -> boolean` | Whether a key is held. Key codes are Windows virtual-key codes (0x41 = "A", 0xA1 = Right Shift). | | `ismouse1pressed() / ismouse2pressed() -> boolean` | Whether the left / right mouse button is held. | | `keypress(keycode) / keyrelease(keycode)` | Presses / releases a key (Roblox must be focused). | | `mouse1press() / mouse1release() / mouse1click()` | The left button. mouse2... for the right one. | | `mousemoveabs(x, y) / mousemoverel(dx, dy)` | Moves the cursor. | | `mousescroll(amount)` | Scrolls the wheel (sending works; reading it doesn't). | | `setrobloxinput(enabled)` | false stops the game from receiving your input; true gives it back. | | `isrbxactive() -> boolean` | Whether the Roblox window is focused. | | `setclipboard(text)` | Copies text. (There is no getclipboard.) | ## Files All paths are relative to Matcha's workspace folder. Not in the official docs, but all present: | Function | Description | | --- | --- | | `readfile(path) -> string` | A file's contents. | | `writefile(path, text)` | Writes a file (some extensions, like .png, are refused). | | `appendfile(path, text)` | Adds to the end of a file. | | `isfile(path) / isfolder(path) -> boolean` | Whether it exists. | | `makefolder(path) / delfolder(path) / delfile(path)` | Folders and deleting. | | `listfiles(path) -> {string}` | A folder's contents. | ## Memory | Function | Description | | --- | --- | | `getbase() -> number` | RobloxPlayerBeta.exe's base address. | | `memory_read(type, address) -> any` | Reads "int", "float", "double", "byte", "string" or "uintptr_t". | | `memory_write(type, address, value)` | Writes one. Wrong types or addresses can crash the game. | > **Note:** `Instance.Address` gives any instance's address in memory. Memory functions need unsafe Luau execution enabled in Matcha's settings. ## Threads and time | Function | Description | | --- | --- | | `task.spawn(fn, ...) / task.defer(fn, ...)` | Runs a function in a new thread. | | `task.wait(seconds?) / task.delay(seconds, fn)` | Waits, or runs later. (Plain wait, spawn and tick exist too.) | ## Not available `Instance.new`, `getgenv`, `getrenv`, `shared`, `hookfunction`, `getrawmetatable`, `newcclosure`, `request / http_request`, `getcustomasset`, `cloneref`, `gethui`, `fireclickdetector`, `firetouchinterest`, `loadfile`, `getclipboard`. Use `game:HttpGet(url)` for web requests and `_G` for shared state. --- # Classes & datatypes > The Roblox classes, properties and types Matcha supports. Anything else reads as nil. | Class | What works | | --- | --- | | DataModel (game) | `PlaceId`, `GameId`, `JobId`, `:GetService(name)`, `:HttpGet(url)`, `:IsLoaded()` | | Instance (all) | `Name`, `ClassName`, `Parent` (writable), `Address`; `:FindFirstChild(name, recursive?)`, `:FindFirstChildOfClass`, `:FindFirstChildWhichIsA`, `:WaitForChild`, `:GetChildren`, `:GetDescendants`, `:IsA`, `:IsDescendantOf`, `:GetFullName`, `:GetAttribute`, `:GetAttributes`, `:SetAttribute` | | Players | `LocalPlayer`, `:GetPlayers()`, `PlayerAdded`, `PlayerRemoving` | | Player | `Name`, `Character`, `Team`, `:GetMouse()` | | Mouse | `X`, `Y` (screen pixels) | | Workspace | `CurrentCamera` | | Camera | `ViewportSize`, `FieldOfView`, `Position`, `lookAt(at, target)` | | BasePart / MeshPart | `Size`, `Position`, `Transparency`, `Color`, `Velocity`, `AssemblyLinearVelocity`, `CanCollide`; MeshPart `MeshId`, `TextureId` | | Model / Humanoid | `PrimaryPart`; `Health`, `MaxHealth` | | ValueBase (IntValue, StringValue...) | `Value` | | GuiObject / TextLabel | `AbsoluteSize`, `AbsolutePosition`; TextLabel `Text` can be read, not written | | HttpService | `:JSONEncode`, `:JSONDecode`, `:GenerateGUID` | | UserInputService | `InputBegan`, `InputEnded` (no `InputChanged`, so no mouse wheel) | | RunService | `Heartbeat`, `RenderStepped` connections | ## Datatypes | Type | Constructors and fields | | --- | --- | | Vector3 | `Vector3.new(x, y, z)`; `X`, `Y`, `Z` | | Vector2 | `Vector2.new(x, y)`; `X`, `Y` | | Color3 | `Color3.new(r, g, b)` (0-1), `fromRGB`, `fromHSV`, `fromHex`; `R`, `G`, `B` | > **Note:** Check before you rely on something: read it and print it. `nil` where you expected a value means Matcha doesn't bind it. --- # Drawing > Matcha's only way to put things on screen. Everything Nova shows is made of these. ```lua Drawing.new(kind: string) -> DrawingObject ``` ```lua local box = Drawing.new("Square") box.Position = Vector2.new(20, 20) box.Size = Vector2.new(200, 120) box.Color = Color3.fromRGB(86, 140, 255) box.Filled = true box.Corner = 8 box.Visible = true task.wait(5) box:Remove() ``` ## Kinds and properties | Kind | Properties | | --- | --- | | All | `Visible`, `Color`, `Transparency` (1 = solid), `ZIndex`, `Position`; `:Remove()` | | Square | `Size`, `Filled`, `Thickness`, `Corner` (rounded corners) | | Line | `From`, `To`, `Thickness` | | Circle | `Radius`, `NumSides`, `Filled`, `Thickness` | | Triangle / Quad | `PointA`, `PointB`, `PointC` (Quad: `PointD`), `Filled` | | Text | `Text`, `Size`, `Font`, `Center`, `Outline`; `TextBounds` (read: the drawn size) | | Image | `Data` (the file's bytes, PNG or JPG), `Size`, `Rounding`. Not in the official docs. | ## Fonts `Drawing.Fonts.UI`, `Drawing.Fonts.System`, `Drawing.Fonts.SystemBold`, `Drawing.Fonts.Minecraft`, `Drawing.Fonts.Monospace`, `Drawing.Fonts.Pixel`, `Drawing.Fonts.Fortnite`, `Drawing.Fonts.ProximaSoftBold` ## Tips - **Reuse objects.** Keep a pool and hide what you don't draw this frame instead of making new ones every frame. - **Only write what changed.** Each property write costs something; compare with the last value first. - **Text sits high.** Matcha draws text about a third of its size above its Position; add `size * 0.3` to line it up. - **Measure text** with a hidden Text object's `TextBounds` to trim or center it. - **Images** come from bytes: `game:HttpGet(url)` or `readfile`. Cache downloads with a non-.png extension. - **Clean up.** Remove every object when your script unloads, or it stays on screen.