---
title: "First module"
description: "Four steps from an empty file to a working module, then editor setup and shared code."
---

> Documentation Index
> Fetch the complete documentation index at: https://aesthetic-docs.pages.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# First module

1. **Declare a module**

   Create `my-module.lua` in `<config>/aesthetic/scripts/`. The game can stay running.

```lua title="my-module.lua"
local module = ui.create("code", "My Module")
```

   `ui.create(icon, name[, description])` takes an icon glyph (`"code"`, `"bolt"`, `"eye"`, …)
   and a name unique among modules. The module lands in the Scripts tab.

2. **Add settings**

```lua
local delay = module:slider("Delay", 1, 100, 20, 1, "t")
local text  = module:input("Text", "hello")
local on    = module:switch("Enabled part", true)
```

   Each call adds one row to the panel, top to bottom in declaration order. Read a value with
   `:get()`. Every control type is on [Settings](/settings).

3. **Handle events**

```lua
local ticks = 0

module:event("enable", function()
ticks = 0
end)

module:event("tick", function()
ticks = ticks + 1
if on:get() and ticks % delay:get() == 0 then
    print(text:get())
end
end)
```

   Names are exact snake_case strings: `"enable"` and `"disable"` fire on toggle, the rest come
   from the game. See [Events](/events).

4. **Save**

   Chat shows `lua » Loaded my-module.lua` and the module appears in the GUI. Enable it and keep
   editing: each save reloads the script, keeping setting values and the enabled state.

## Editor support

Every launch writes language-server files next to your scripts:

| Path | Contents |
|------|----------|
| `scripts/library/*.lua` | LuaCATS stubs for the whole API |
| `scripts/docs/*.md` | Offline copy of this reference |
| `scripts/.luarc.json` | Points [lua-language-server](https://luals.github.io/) at the stubs |

`library/` and `docs/` are overwritten on every launch, so edits there are lost. `.luarc.json`
is only written when missing.

Open the `scripts` folder in an editor running lua-language-server for completion, hovers,
signature help and typed event callbacks:

### VS Code

Install the **Lua** extension (sumneko), then open the `scripts` folder. `.luarc.json` is
picked up automatically.

### IntelliJ

Install the **SumnekoLua** plugin and open the `scripts` folder as a project.

### Neovim

Enable `lua_ls` (for example through nvim-lspconfig) and start Neovim inside the `scripts`
folder, so the server finds `.luarc.json` at the workspace root.

## Debug output

| Call | Goes to |
|------|---------|
| `print("a", value)` | Chat, with a `lua »` prefix |
| `aesthetic.log(msg)` | Game log, prefixed with the script file name |

## Shared code

`require` resolves relative to the scripts folder. Only top-level `.lua` files load as
scripts, so shared code goes in a subfolder:

```lua
local util = require "lib/util"   -- <scripts>/lib/util.lua

module:event("enable", function()
util.greet()
end)
```

```lua
local M = {}

function M.greet()
print("hello from lib")
end

return M
```

Source: https://aesthetic-docs.pages.dev/getting-started/index.md
