# Technical Documentation: kitao/pyxel

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/kitao/pyxel)

### Overview (`README.md`)

# <a href="https://kitao.dev/pyxel/"><img src="https://kitao.dev/pyxel/assets/images/pyxel_logo_152x64.png" alt="Pyxel" width="152" height="64"></a>

<p>
  <a href="https://github.com/kitao/pyxel"><img src="https://img.shields.io/github/stars/kitao/pyxel?style=social" alt="GitHub Repo stars" height="20"></a>
  <a href="https://github.com/sponsors/kitao"><img src="https://img.shields.io/github/sponsors/kitao?label=Sponsor%20me&amp;logo=github%20sponsors&amp;style=social" alt="GitHub Sponsors" height="20"></a>
  <a href="https://www.patreon.com/tkitao"><img src="https://img.shields.io/badge/Support%20me%20on%20Patreon-000000?style=social&amp;logo=patreon" alt="Patreon" height="20"></a>
  <a href="https://ko-fi.com/H2H27VDKD"><img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="ko-fi" height="20" width="149"></a>
</p>

A retro game engine for Python

**[▸ Website](https://kitao.dev/pyxel/)**

<p>
        <a href="https://kitao.dev/pyxel/user-examples/"
          ><img
            src="https://kitao.dev/pyxel/assets/images/pyxel_thanks.png"
            alt="Pyxel User Examples"
            width="553"
        /></a>
        <a href="https://gihyo.jp/book/2025/978-4-297-14657-3"
          ><img
            src="https://kitao.dev/pyxel/assets/images/pyxel_book.png"
            alt="Pyxel book"
            width="190"
        /></a>
      </p>

<p>
        <a href="https://kitao.dev/pyxel/showcase/examples/02-jump-game.html">
          <img
            src="https://kitao.dev/pyxel/assets/images/02_jump_game.gif"
            alt="Jump game"
            width="253"
          />
        </a>
        <a href="https://kitao.dev/pyxel/showcase/examples/10-platformer.html">
          <img
            src="https://kitao.dev/pyxel/assets/images/10_platformer.gif"
            alt="Platformer"
            width="222"
          />
        </a>
        <a href="https://kitao.dev/pyxel/showcase/apps/30sec-of-daylight.html">
          <img
            src="https://kitao.dev/pyxel/assets/images/30sec_of_daylight.gif"
            alt="30 Seconds of Daylight"
            width="268"
          />
        </a>
        <a href="https://kitao.dev/pyxel/showcase/apps/megaball.html">
          <img src="https://kitao.dev/pyxel/assets/images/megaball.gif" alt="Megaball" width="231" />
        </a>
        <a href="https://kitao.dev/pyxel/showcase/tools/image-editor.html">
          <img
            src="https://kitao.dev/pyxel/assets/images/image_tilemap_editor.gif"
            alt="Image and tilemap editor"
            width="256"
          />
        </a>
        <a href="https://kitao.dev/pyxel/showcase/tools/sound-editor.html">
          <img
            src="https://kitao.dev/pyxel/assets/images/sound_music_editor.gif"
            alt="Sound and music editor"
            width="256"
          />
        </a>
        <a
          href="https://kitao.dev/pyxel/showcase/examples/c04-mesh-and-motion.html"
        >
          <img
            src="https://kitao.dev/pyxel/assets/images/c04_mesh_and_motion.gif"
            alt="Mesh and motion"
            width="248"
          />
        </a>
        <a
          href="https://kitao.dev/pyxel/showcase/examples/c05-3d-collision.html"
        >
          <img
            src="https://kitao.dev/pyxel/assets/images/c05_3d_collision.gif"
            alt="3D collision"
            width="248"
          />
        </a>
        <a href="https://kitao.dev/pyxel/showcase/examples/c06-3d-physics.html">
          <img
            src="https://kitao.dev/pyxel/assets/images/c06_3d_physics.gif"
            alt="3D physics"
            width="248"
          />
        </a>
      </p>

---

### Api Reference (`docs/api-reference.md`)

# Pyxel API Reference

*This document was auto-generated from the [Pyxel API Reference](https://kitao.github.io/pyxel/web/api-reference/) web page, which also offers multilingual support.*

## System

### `width` — variable

The width of the screen.

- **Type:** `int`

### `height` — variable

The height of the screen.

- **Type:** `int`

### `frame_count` — variable

The number of elapsed frames.

- **Type:** `int`

### `init(width, height, title="Pyxel", fps=30, quit_key=KEY_ESCAPE, display_scale=None, capture_scale=2, capture_sec=10, headless=False)` — function

Initialize the Pyxel application with the screen size (width, height).

**Parameters:**

- `width` (*int*) — Screen width
- `height` (*int*) — Screen height
- `title` (*str*) — Window title. Defaults to "Pyxel".
- `fps` (*int*) — Frame rate. Defaults to 30.
- `quit_key` (*int*) — Key to quit the application. Defaults to KEY_ESCAPE.
- `display_scale` (*int*) — Display scale factor. If omitted, automatically determined.
- `capture_scale` (*int*) — Screen capture scale factor. Defaults to 2.
- `capture_sec` (*int*) — Maximum recording time for screen capture video. Defaults to 10.
- `headless` (*bool*) — Run without a window. Defaults to False.

**Example:**

```python
pyxel.init(
    160,
    120,
    title="My Pyxel App",
    fps=60,
    quit_key=pyxel.KEY_NONE,
    capture_scale=3,
    capture_sec=0,
)
```

### `run(update, draw)` — function

Start the Pyxel application and call the update function for frame update and the draw function for drawing.

**Parameters:**

- `update` (*callable*) — Function to update game logic each frame
- `draw` (*callable*) — Function to draw the screen each frame

### `show()` — function

Show the screen and wait until the quit key is pressed.

### `flip()` — function

Refresh the screen by one frame. The application exits when the quit key is pressed. This function is not available in the web version.

### `quit()` — function

Quit the Pyxel application.

### `reset()` — function

Restart the Pyxel application from the beginning.

### `title(title)` — function *(Advanced)*

Set the window title.

**Parameters:**

- `title` (*str*) — Window title

### `icon(data, scale, colkey=None)` — function *(Advanced)*

Set the application icon. Specify the icon image as a list of strings.

**Parameters:**

- `data` (*list[str]*) — Icon image as a list of strings
- `scale` (*int*) — Scale factor
- `colkey` (*int/None*) — Transparent color. If omitted, no transparency.

### `fullscreen(enabled)` — function *(Advanced)*

Set whether to run in fullscreen mode.

**Parameters:**

- `enabled` (*bool*) — Enable fullscreen

### `resize(width, height)` — function *(Advanced)*

Change the screen size at runtime.

**Parameters:**

- `width` (*int*) — New screen width in pixels
- `height` (*int*) — New screen height in pixels

### `screen_mode(scr)` — function *(Advanced)*

Set the screen mode (0: crisp, 1: smooth, 2: retro).

**Parameters:**

- `scr` (*int*) — Screen mode number (0: crisp, 1: smooth, 2: retro)

### `perf_monitor(enabled)` — function *(Advanced)*

Show or hide the performance monitor.

**Parameters:**

- `enabled` (*bool*) — Enable the performance monitor

### `integer_scale(enabled)` — function *(Advanced)*

Enable integer scaling for the display.

**Parameters:**

- `enabled` (*bool*) — Enable integer scaling

## Resource

### `load(filename, exclude_images=False, exclude_tilemaps=False, exclude_sounds=False, exclude_musics=False)` — function

Load the resource file (.pyxres). If an option is set to True, the corresponding resource will be excluded from loading.

**Parameters:**

- `filename` (*str*) — Resource file name
- `exclude_images` (*bool*) — Exclude image banks. Defaults to False.
- `exclude_tilemaps` (*bool*) — Exclude tilemaps. Defaults to False.
- `exclude_sounds` (*bool*) — Exclude sounds. Defaults to False.
- `exclude_musics` (*bool*) — Exclude music tracks. Defaults to False.

**Note:** If a palette file (.pyxpal) with the same name exists, the palette display colors will also be updated.

### `user_data_dir(vendor_name, app_name)` — function *(Advanced)*

Return the user data directory created based on vendor_name and app_name. If the directory does not exist, it will be created automatically.

**Parameters:**

- `vendor_name` (*str*) — Vendor name
- `app_name` (*str*) — Application name

**Returns:** `str` — Path to the user data directory

**Example:**

```python
pyxel.user_data_dir("Takashi Kitao", "Pyxel Shooter")
```

### `save(filename, exclude_images=False, exclude_tilemaps=False, exclude_sounds=False, exclude_musics=False)` — function *(Advanced)*

Save the resource file (.pyxres). If an option is set to True, the corresponding resource will be excluded from saving.

**Parameters:**

- `filename` (*str*) — Resource file name
- `exclude_images` (*bool*) — Exclude image banks. Defaults to False.
- `exclude_tilemaps` (*bool*) — Exclude tilemaps. Defaults to False.
- `exclude_sounds` (*bool*) — Exclude sounds. Defaults to False.
- `exclude_musics` (*bool*) — Exclude music tracks. Defaults to False.

### `screenshot(filename=None, scale=2)` — function *(Advanced)*

Take a screenshot.

**Parameters:**

- `filename` (*str/None*) — File name. If omitted, saved to desktop.
- `scale` (*int/None*) — Scale factor. Defaults to capture_scale.

### `screencast(filename=None, scale=2)` — function *(Advanced)*

Save the screen recording as a GIF file.

**Parameters:**

- `filename` (*str/None*) — File name. If omitted, saved to desktop.
- `scale` (*int/None*) — Scale factor. Defaults to capture_scale.

### `reset_screencast()` — function *(Advanced)*

Reset the screen recording buffer.

### `load_pal(filename)` — function *(Advanced)*

Load a palette file (.pyxpal).

**Parameters:**

- `filename` (*str*) — Palette file name

### `save_pal(filename)` — function *(Advanced)*

Save a palette file (.pyxpal).

**Parameters:**

- `filename` (*str*) — Palette file name

## Input

### `mouse_x` — variable

The current x position of the mouse cursor.

- **Type:** `int`

### `mouse_y` — variable

The current y position of the mouse cursor.

- **Type:** `int`

### `mouse_wheel` — variable

The current value of the mouse wheel.

- **Type:** `int`

### `input_keys` — variable *(Advanced)*

List of keys input in the current frame.

- **Type:** `list[int]`

### `input_text` — variable *(Advanced)*

Text input in the current frame.

- **Type:** `str`

### `dropped_files` — variable *(Advanced)*

List of files dropped in the current frame.

- **Type:** `list[str]`

### `btn(key)` — function

Return True if the key is pressed, otherwise return False.

**Parameters:**

- `key` (*int*) — Key code

**Returns:** `bool` — True if pressed

### `btnp(key, hold=0, repeat=0)` — function

Return True if the key is pressed in that frame. When hold and repeat are specified, after holding the key for hold frames, return True every repeat frames.

**Parameters:**

- `key` (*int*) — Key code
- `hold` (*int*) — Frames to hold before repeat starts. Defaults to 0.
- `repeat` (*int*) — Repeat interval in frames. If 0, no repeat.

**Returns:** `bool` — True if pressed in that frame

### `btnr(key)` — function

Return True if the key is released in that frame, otherwise return False.

**Parameters:**

- `key` (*int*) — Key code

**Returns:** `bool` — True if released in that frame

### `mouse(visible)` — function

Show the mouse cursor if visible is True, and hide it if False. The cursor position continues to update even when hidden.

**Parameters:**

- `visible` (*bool*) — Show or hide the cursor

### `btnv(key)` — function *(Advanced)*

Return the analog value of the specified key (e.g., gamepad axis value).

**Parameters:**

- `key` (*int*) — Key code

**Returns:** `int` — Analog value of the key

### `set_btn(key, state)` — function *(Advanced)*

Set the press/release state of the specified key. Mainly for headless mode input simulation.

**Parameters:**

- `key` (*int*) — Target key
- `state` (*bool*) — True for press, False for release

### `set_btnv(key, val)` — function *(Advanced)*

Set the analog value of the specified key. Mainly for headless mode input simulation.

**Parameters:**

- `key` (*int*) — Target key
- `val` (*int*) — Analog value to set

### `set_mouse_pos(x, y)` — function *(Advanced)*

Set the mouse cursor position. Mainly for headless mode input simulation.

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate

### `set_input_text(text)` — function *(Advanced)*

Set the text input for the current frame. Replaces any existing text. Mainly for headless mode input simulation.

**Parameters:**

- `text` (*str*) — Text input

### `set_dropped_files(files)` — function *(Advanced)*

Set the dropped file list for the current frame. Replaces any existing list. Mainly for headless mode input simulation.

**Parameters:**

- `files` (*list[str]*) — List of file paths

### Key Constants

**Special Keys:**

`KEY_UNKNOWN` `KEY_RETURN` `KEY_ESCAPE` `KEY_BACKSPACE` `KEY_TAB` `KEY_SPACE` `KEY_NONE`

**Letters:**

`KEY_A` `KEY_B` `KEY_C` `KEY_D` `KEY_E` `KEY_F` `KEY_G` `KEY_H` `KEY_I` `KEY_J` `KEY_K` `KEY_L` `KEY_M` `KEY_N` `KEY_O` `KEY_P` `KEY_Q` `KEY_R` `KEY_S` `KEY_T` `KEY_U` `KEY_V` `KEY_W` `KEY_X` `KEY_Y` `KEY_Z`

**Numbers:**

`KEY_0` `KEY_1` `KEY_2` `KEY_3` `KEY_4` `KEY_5` `KEY_6` `KEY_7` `KEY_8` `KEY_9`

**Symbols:**

`KEY_EXCLAIM` `KEY_QUOTEDBL` `KEY_HASH` `KEY_PERCENT` `KEY_DOLLAR` `KEY_AMPERSAND` `KEY_QUOTE` `KEY_LEFTPAREN` `KEY_RIGHTPAREN` `KEY_ASTERISK` `KEY_PLUS` `KEY_COMMA` `KEY_MINUS` `KEY_PERIOD` `KEY_SLASH` `KEY_COLON` `KEY_SEMICOLON` `KEY_LESS` `KEY_EQUALS` `KEY_GREATER` `KEY_QUESTION` `KEY_AT` `KEY_LEFTBRACKET` `KEY_BACKSLASH` `KEY_RIGHTBRACKET` `KEY_CARET` `KEY_UNDERSCORE` `KEY_BACKQUOTE`

**Function:**

`KEY_F1` `KEY_F2` `KEY_F3` `KEY_F4` `KEY_F5` `KEY_F6` `KEY_F7` `KEY_F8` `KEY_F9` `KEY_F10` `KEY_F11` `KEY_F12` `KEY_F13` `KEY_F14` `KEY_F15` `KEY_F16` `KEY_F17` `KEY_F18` `KEY_F19` `KEY_F20` `KEY_F21` `KEY_F22` `KEY_F23` `KEY_F24`

**Navigation:**

`KEY_INSERT` `KEY_HOME` `KEY_PAGEUP` `KEY_DELETE` `KEY_END` `KEY_PAGEDOWN` `KEY_RIGHT` `KEY_LEFT` `KEY_DOWN` `KEY_UP`

**Modifiers:**

`KEY_CAPSLOCK` `KEY_LCTRL` `KEY_LSHIFT` `KEY_LALT` `KEY_LGUI` `KEY_RCTRL` `KEY_RSHIFT` `KEY_RALT` `KEY_RGUI` `KEY_SHIFT` `KEY_CTRL` `KEY_ALT` `KEY_GUI`

**Numpad:**

`KEY_NUMLOCKCLEAR` `KEY_KP_DIVIDE` `KEY_KP_MULTIPLY` `KEY_KP_MINUS` `KEY_KP_PLUS` `KEY_KP_ENTER` `KEY_KP_1` `KEY_KP_2` `KEY_KP_3` `KEY_KP_4` `KEY_KP_5` `KEY_KP_6` `KEY_KP_7` `KEY_KP_8` `KEY_KP_9` `KEY_KP_0` `KEY_KP_PERIOD` `KEY_KP_EQUALS` `KEY_KP_COMMA` `KEY_KP_EQUALSAS400` `KEY_KP_00` `KEY_KP_000` `KEY_KP_LEFTPAREN` `KEY_KP_RIGHTPAREN` `KEY_KP_LEFTBRACE` `KEY_KP_RIGHTBRACE` `KEY_KP_TAB` `KEY_KP_BACKSPACE` `KEY_KP_A` `KEY_KP_B` `KEY_KP_C` `KEY_KP_D` `KEY_KP_E` `KEY_KP_F` `KEY_KP_XOR` `KEY_KP_POWER` `KEY_KP_PERCENT` `KEY_KP_LESS` `KEY_KP_GREATER` `KEY_KP_AMPERSAND` `KEY_KP_DBLAMPERSAND` `KEY_KP_VERTICALBAR` `KEY_KP_DBLVERTICALBAR` `KEY_KP_COLON` `KEY_KP_HASH` `KEY_KP_SPACE` `KEY_KP_AT` `KEY_KP_EXCLAM` `KEY_KP_MEMSTORE` `KEY_KP_MEMRECALL` `KEY_KP_MEMCLEAR` `KEY_KP_MEMADD` `KEY_KP_MEMSUBTRACT` `KEY_KP_MEMMULTIPLY` `KEY_KP_MEMDIVIDE` `KEY_KP_PLUSMINUS` `KEY_KP_CLEAR` `KEY_KP_CLEARENTRY` `KEY_KP_BINARY` `KEY_KP_OCTAL` `KEY_KP_DECIMAL` `KEY_KP_HEXADECIMAL`

**System & Media:**

`KEY_PRINTSCREEN` `KEY_SCROLLLOCK` `KEY_PAUSE` `KEY_APPLICATION` `KEY_POWER` `KEY_EXECUTE` `KEY_HELP` `KEY_MENU` `KEY_SELECT` `KEY_STOP` `KEY_AGAIN` `KEY_UNDO` `KEY_CUT` `KEY_COPY` `KEY_PASTE` `KEY_FIND` `KEY_MUTE` `KEY_VOLUMEUP` `KEY_VOLUMEDOWN` `KEY_ALTERASE` `KEY_SYSREQ` `KEY_CANCEL` `KEY_CLEAR` `KEY_PRIOR` `KEY_RETURN2` `KEY_SEPARATOR` `KEY_OUT` `KEY_OPER` `KEY_CLEARAGAIN` `KEY_CRSEL` `KEY_EXSEL` `KEY_THOUSANDSSEPARATOR` `KEY_DECIMALSEPARATOR` `KEY_CURRENCYUNIT` `KEY_CURRENCYSUBUNIT`

### Mouse Constants

**Cursor & Wheel:**

`MOUSE_POS_X` `MOUSE_POS_Y` `MOUSE_WHEEL_X` `MOUSE_WHEEL_Y`

**Buttons:**

`MOUSE_BUTTON_LEFT` `MOUSE_BUTTON_MIDDLE` `MOUSE_BUTTON_RIGHT` `MOUSE_BUTTON_X1` `MOUSE_BUTTON_X2`

### Gamepad Constants

**Gamepad 1:**

`GAMEPAD1_AXIS_LEFTX` `GAMEPAD1_AXIS_LEFTY` `GAMEPAD1_AXIS_RIGHTX` `GAMEPAD1_AXIS_RIGHTY` `GAMEPAD1_AXIS_TRIGGERLEFT` `GAMEPAD1_AXIS_TRIGGERRIGHT` `GAMEPAD1_BUTTON_A` `GAMEPAD1_BUTTON_B` `GAMEPAD1_BUTTON_X` `GAMEPAD1_BUTTON_Y` `GAMEPAD1_BUTTON_BACK` `GAMEPAD1_BUTTON_GUIDE` `GAMEPAD1_BUTTON_START` `GAMEPAD1_BUTTON_LEFTSTICK` `GAMEPAD1_BUTTON_RIGHTSTICK` `GAMEPAD1_BUTTON_LEFTSHOULDER` `GAMEPAD1_BUTTON_RIGHTSHOULDER` `GAMEPAD1_BUTTON_DPAD_UP` `GAMEPAD1_BUTTON_DPAD_DOWN` `GAMEPAD1_BUTTON_DPAD_LEFT` `GAMEPAD1_BUTTON_DPAD_RIGHT`

**Gamepad 2:**

`GAMEPAD2_AXIS_LEFTX` `GAMEPAD2_AXIS_LEFTY` `GAMEPAD2_AXIS_RIGHTX` `GAMEPAD2_AXIS_RIGHTY` `GAMEPAD2_AXIS_TRIGGERLEFT` `GAMEPAD2_AXIS_TRIGGERRIGHT` `GAMEPAD2_BUTTON_A` `GAMEPAD2_BUTTON_B` `GAMEPAD2_BUTTON_X` `GAMEPAD2_BUTTON_Y` `GAMEPAD2_BUTTON_BACK` `GAMEPAD2_BUTTON_GUIDE` `GAMEPAD2_BUTTON_START` `GAMEPAD2_BUTTON_LEFTSTICK` `GAMEPAD2_BUTTON_RIGHTSTICK` `GAMEPAD2_BUTTON_LEFTSHOULDER` `GAMEPAD2_BUTTON_RIGHTSHOULDER` `GAMEPAD2_BUTTON_DPAD_UP` `GAMEPAD2_BUTTON_DPAD_DOWN` `GAMEPAD2_BUTTON_DPAD_LEFT` `GAMEPAD2_BUTTON_DPAD_RIGHT`

**Gamepad 3:**

`GAMEPAD3_AXIS_LEFTX` `GAMEPAD3_AXIS_LEFTY` `GAMEPAD3_AXIS_RIGHTX` `GAMEPAD3_AXIS_RIGHTY` `GAMEPAD3_AXIS_TRIGGERLEFT` `GAMEPAD3_AXIS_TRIGGERRIGHT` `GAMEPAD3_BUTTON_A` `GAMEPAD3_BUTTON_B` `GAMEPAD3_BUTTON_X` `GAMEPAD3_BUTTON_Y` `GAMEPAD3_BUTTON_BACK` `GAMEPAD3_BUTTON_GUIDE` `GAMEPAD3_BUTTON_START` `GAMEPAD3_BUTTON_LEFTSTICK` `GAMEPAD3_BUTTON_RIGHTSTICK` `GAMEPAD3_BUTTON_LEFTSHOULDER` `GAMEPAD3_BUTTON_RIGHTSHOULDER` `GAMEPAD3_BUTTON_DPAD_UP` `GAMEPAD3_BUTTON_DPAD_DOWN` `GAMEPAD3_BUTTON_DPAD_LEFT` `GAMEPAD3_BUTTON_DPAD_RIGHT`

**Gamepad 4:**

`GAMEPAD4_AXIS_LEFTX` `GAMEPAD4_AXIS_LEFTY` `GAMEPAD4_AXIS_RIGHTX` `GAMEPAD4_AXIS_RIGHTY` `GAMEPAD4_AXIS_TRIGGERLEFT` `GAMEPAD4_AXIS_TRIGGERRIGHT` `GAMEPAD4_BUTTON_A` `GAMEPAD4_BUTTON_B` `GAMEPAD4_BUTTON_X` `GAMEPAD4_BUTTON_Y` `GAMEPAD4_BUTTON_BACK` `GAMEPAD4_BUTTON_GUIDE` `GAMEPAD4_BUTTON_START` `GAMEPAD4_BUTTON_LEFTSTICK` `GAMEPAD4_BUTTON_RIGHTSTICK` `GAMEPAD4_BUTTON_LEFTSHOULDER` `GAMEPAD4_BUTTON_RIGHTSHOULDER` `GAMEPAD4_BUTTON_DPAD_UP` `GAMEPAD4_BUTTON_DPAD_DOWN` `GAMEPAD4_BUTTON_DPAD_LEFT` `GAMEPAD4_BUTTON_DPAD_RIGHT`

## Graphics

### `colors` — variable

List of the palette display colors. Specified by 24-bit numerical value. Supports Python list operations.

- **Type:** `list[int]`

**Example:**

```python
old_colors = list(pyxel.colors)
pyxel.colors[15] = 0x112233
```

### `images` — variable

Image banks (Image instances) (0-2).

- **Type:** `list[Image]`

**Example:**

```python
pyxel.images[0].load(0, 0, "title.png")
```

### `tilemaps` — variable

Tilemaps (Tilemap instances) (0-7).

- **Type:** `list[Tilemap]`

### `screen` — variable *(Advanced)*

The screen image (Image class instance).

- **Type:** `Image`

### `cursor` — variable *(Advanced)*

The cursor image (Image class instance).

- **Type:** `Image`

### `font` — variable *(Advanced)*

The font image (Image class instance).

- **Type:** `Image`

### `clip(x, y, w, h)` — function

Set the drawing area of the screen from (x, y) with a width of w and a height of h. Call clip() to reset to full screen.

**Parameters:**

- `x` (*float*) — X coordinate of the upper-left corner
- `y` (*float*) — Y coordinate of the upper-left corner
- `w` (*float*) — Width of the clipping area
- `h` (*float*) — Height of the clipping area

### `clip()` — function

Reset the drawing area to full screen.

### `camera(x, y)` — function

Set the drawing offset to (x, y). All subsequent drawing operations will be shifted by (-x, -y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate

### `camera()` — function

Reset the drawing offset to (0, 0).

### `pal(col1, col2)` — function

Replace color col1 with col2 when drawing.

**Parameters:**

- `col1` (*int*) — Color to replace
- `col2` (*int*) — Replacement color

### `pal()` — function

Reset the palette to the initial state.

### `dither(alpha)` — function

Apply dithering (pseudo-transparency) when drawing. Set alpha in the range 0.0-1.0.

**Parameters:**

- `alpha` (*float*) — Opacity (0.0: transparent, 1.0: opaque)

### `cls(col)` — function

Clear the screen with color col.

**Parameters:**

- `col` (*int*) — Color

### `pget(x, y)` — function

Get the color of the pixel at (x, y). The coordinates are absolute and not affected by the camera offset.

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate

**Returns:** `int` — Color of the pixel

### `pset(x, y, col)` — function

Draw a pixel of color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `col` (*int*) — Color

### `line(x1, y1, x2, y2, col)` — function

Draw a line of color col from (x1, y1) to (x2, y2).

**Parameters:**

- `x1` (*float*) — Start X coordinate
- `y1` (*float*) — Start Y coordinate
- `x2` (*float*) — End X coordinate
- `y2` (*float*) — End Y coordinate
- `col` (*int*) — Color

### `rect(x, y, w, h, col)` — function

Draw a filled rectangle of width w, height h, and color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `w` (*float*) — Width
- `h` (*float*) — Height
- `col` (*int*) — Color

### `rectb(x, y, w, h, col)` — function

Draw the outline of a rectangle of width w, height h, and color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `w` (*float*) — Width
- `h` (*float*) — Height
- `col` (*int*) — Color

### `circ(x, y, r, col)` — function

Draw a filled circle of radius r and color col at (x, y).

**Parameters:**

- `x` (*float*) — Center X coordinate
- `y` (*float*) — Center Y coordinate
- `r` (*float*) — Radius
- `col` (*int*) — Color

### `circb(x, y, r, col)` — function

Draw the outline of a circle of radius r and color col at (x, y).

**Parameters:**

- `x` (*float*) — Center X coordinate
- `y` (*float*) — Center Y coordinate
- `r` (*float*) — Radius
- `col` (*int*) — Color

### `elli(x, y, w, h, col)` — function

Draw a filled ellipse of width w, height h, and color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `w` (*float*) — Width
- `h` (*float*) — Height
- `col` (*int*) — Color

### `ellib(x, y, w, h, col)` — function

Draw the outline of an ellipse of width w, height h, and color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `w` (*float*) — Width
- `h` (*float*) — Height
- `col` (*int*) — Color

### `tri(x1, y1, x2, y2, x3, y3, col)` — function

Draw a filled triangle with vertices (x1, y1), (x2, y2), (x3, y3) and color col.

**Parameters:**

- `x1` (*float*) — Vertex 1 X
- `y1` (*float*) — Vertex 1 Y
- `x2` (*float*) — Vertex 2 X
- `y2` (*float*) — Vertex 2 Y
- `x3` (*float*) — Vertex 3 X
- `y3` (*float*) — Vertex 3 Y
- `col` (*int*) — Color

### `trib(x1, y1, x2, y2, x3, y3, col)` — function

Draw the outline of a triangle with vertices (x1, y1), (x2, y2), (x3, y3) and color col.

**Parameters:**

- `x1` (*float*) — Vertex 1 X
- `y1` (*float*) — Vertex 1 Y
- `x2` (*float*) — Vertex 2 X
- `y2` (*float*) — Vertex 2 Y
- `x3` (*float*) — Vertex 3 X
- `y3` (*float*) — Vertex 3 Y
- `col` (*int*) — Color

### `fill(x, y, col)` — function

Fill the area connected with the same color as (x, y) with color col.

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `col` (*int*) — Fill color

### `blt(x, y, img, u, v, w, h, colkey=None, rotate=0, scale=1)` — function

Copy the region of size (w, h) from (u, v) of image bank img (0-2 or Image instance) to (x, y). Negative w/h flips the image. colkey sets the transparent color. rotate and scale apply transformations.

**Parameters:**

- `x` (*float*) — Destination X
- `y` (*float*) — Destination Y
- `img` (*int/Image*) — Image bank number (0-2) or Image instance
- `u` (*float*) — Source X in the image bank
- `v` (*float*) — Source Y in the image bank
- `w` (*float*) — Width (negative to flip)
- `h` (*float*) — Height (negative to flip)
- `colkey` (*int/None*) — Transparent color. If omitted, no transparency.
- `rotate` (*float*) — Rotation angle in degrees (centered on the copy region). Defaults to 0.
- `scale` (*float*) — Scale factor (centered on the copy region). Defaults to 1.

### `bltm(x, y, tm, u, v, w, h, colkey=None, rotate=0, scale=1)` — function

Copy the region of size (w, h) from (u, v) of tilemap tm (0-7 or Tilemap instance) to (x, y). Each tile is 8x8 pixels stored as (image_tx, image_ty). Negative w/h flips the image. colkey sets the transparent color. rotate and scale apply transformations.

**Parameters:**

- `x` (*float*) — Destination X
- `y` (*float*) — Destination Y
- `tm` (*int/Tilemap*) — Tilemap number (0-7) or Tilemap instance
- `u` (*float*) — Source X in the tilemap
- `v` (*float*) — Source Y in the tilemap
- `w` (*float*) — Width (negative to flip)
- `h` (*float*) — Height (negative to flip)
- `colkey` (*int/None*) — Transparent color. If omitted, no transparency.
- `rotate` (*float*) — Rotation angle in degrees (centered on the copy region). Defaults to 0.
- `scale` (*float*) — Scale factor (centered on the copy region). Defaults to 1.

### `blt3d(x, y, w, h, img, pos, rot, fov=60, colkey=None)` — function *(Advanced)*

Draw the image bank img (0-2 or Image instance) with perspective projection onto the screen rectangle (x, y, w, h). pos is the camera position where x, y match 2D coordinates and z is height. rot is the rotation in degrees. fov sets the field of view in degrees. colkey sets the transparent color.

**Parameters:**

- `x` (*float*) — Destination X
- `y` (*float*) — Destination Y
- `w` (*float*) — Display width
- `h` (*float*) — Display height
- `img` (*int/Image*) — Image bank number (0-2) or Image instance
- `pos` (*(float, float, float)*) — Camera position (x, y, z). x, y match 2D coordinates, z is height
- `rot` (*(float, float, float)*) — Rotation in degrees. rot_x is vertical, rot_y is horizontal, rot_z is tilt
- `fov` (*float*) — Field of view in degrees. Defaults to 60.
- `colkey` (*int/None*) — Transparent color. If omitted, no transparency.

### `bltm3d(x, y, w, h, tm, pos, rot, fov=60, colkey=None)` — function *(Advanced)*

Draw the tilemap tm (0-7 or Tilemap instance) with perspective projection onto the screen rectangle (x, y, w, h). pos is the camera position where x, y match 2D coordinates and z is height. rot is the rotation in degrees. fov sets the field of view in degrees. colkey sets the transparent color.

**Parameters:**

- `x` (*float*) — Destination X
- `y` (*float*) — Destination Y
- `w` (*float*) — Display width
- `h` (*float*) — Display height
- `tm` (*int/Tilemap*) — Tilemap number (0-7) or Tilemap instance
- `pos` (*(float, float, float)*) — Camera position (x, y, z). x, y match 2D coordinates, z is height
- `rot` (*(float, float, float)*) — Rotation in degrees. rot_x is vertical, rot_y is horizontal, rot_z is tilt
- `fov` (*float*) — Field of view in degrees. Defaults to 60.
- `colkey` (*int/None*) — Transparent color. If omitted, no transparency.

### `text(x, y, s, col, font=None)` — function

Draw a string s in color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `s` (*str*) — String to draw
- `col` (*int*) — Color
- `font` (*Font*) — Custom font. If omitted, the standard font is used.

### Color Constants

- `COLOR_BLACK` — 0 — Black (#000000)
- `COLOR_NAVY` — 1 — Navy (#2b335f)
- `COLOR_PURPLE` — 2 — Purple (#7e2072)
- `COLOR_GREEN` — 3 — Green (#19959c)
- `COLOR_BROWN` — 4 — Brown (#8b4852)
- `COLOR_DARK_BLUE` — 5 — Dark Blue (#395c98)
- `COLOR_LIGHT_BLUE` — 6 — Light Blue (#a9c1ff)
- `COLOR_WHITE` — 7 — White (#eeeeee)
- `COLOR_RED` — 8 — Red (#d4186c)
- `COLOR_ORANGE` — 9 — Orange (#d38441)
- `COLOR_YELLOW` — 10 — Yellow (#e9c35b)
- `COLOR_LIME` — 11 — Lime (#70c6a9)
- `COLOR_CYAN` — 12 — Cyan (#7696de)
- `COLOR_GRAY` — 13 — Gray (#a3a3a3)
- `COLOR_PINK` — 14 — Pink (#ff9798)
- `COLOR_PEACH` — 15 — Peach (#edc7b0)

### Resource Constants

- `NUM_COLORS` — 16 — Number of palette colors
- `NUM_IMAGES` — 3 — Number of image banks (0-2)
- `IMAGE_SIZE` — 256 — Image width and height
- `NUM_TILEMAPS` — 8 — Number of tilemaps (0-7)
- `TILEMAP_SIZE` — 256 — Tilemap width and height
- `TILE_SIZE` — 8 — Tile size in pixels
- `FONT_WIDTH` — 4 — Built-in font width
- `FONT_HEIGHT` — 6 — Built-in font height

## Audio

### `channels` — variable *(Advanced)*

Audio channels (Channel instances) (0-3).

- **Type:** `list[Channel]`

### `tones` — variable *(Advanced)*

Tone definitions (Tone instances) (0-3).

- **Type:** `list[Tone]`

### `sounds` — variable

Sounds (Sound instances) (0-63).

- **Type:** `list[Sound]`

**Example:**

```python
pyxel.sounds[0].speed = 60
```

### `musics` — variable

Music tracks (Music instances) (0-7).

- **Type:** `list[Music]`

### `play(ch, snd, sec=0, loop=False, resume=False)` — function

Play the sound snd on channel ch (0-3). snd can be a sound number (0-63), a list of numbers, a Sound instance, a list of Sounds, or an MML string.

**Parameters:**

- `ch` (*int*) — Channel number (0-3)
- `snd` (*int/list/Sound/str*) — Sound number (0-63), list of numbers, Sound instance, list of Sounds, or MML string
- `sec` (*float*) — Playback start position in seconds. Defaults to 0.
- `loop` (*bool*) — Loop playback. Defaults to False.
- `resume` (*bool*) — Resume previous sound after playback ends. Defaults to False.

### `playm(msc, sec=0, loop=False)` — function

Play the music msc (0-7).

**Parameters:**

- `msc` (*int*) — Music number (0-7)
- `sec` (*float*) — Playback start position in seconds. Defaults to 0.
- `loop` (*bool*) — Loop playback. Defaults to False.

### `stop(ch)` — function

Stop playback of the specified channel ch (0-3).

**Parameters:**

- `ch` (*int*) — Channel number (0-3)

### `stop()` — function

Stop playback of all channels.

### `play_pos(ch)` — function

Get the sound playback position of channel ch (0-3) as a tuple of (sound_index, sec). Return None when playback has stopped.

**Parameters:**

- `ch` (*int*) — Channel number (0-3)

**Returns:** `tuple[int, float]/None` — (sound_index, sec) or None

### `gen_bgm(preset, transp, instr, seed, play=False)` — function

Generate a BGM MML list using an algorithm. preset (0-7) selects the preset, instr (0-3) selects the instrumentation.

**Parameters:**

- `preset` (*int*) — Preset number (0-7). 0-1: title, departure (medium tempo), 2-3: town, peaceful (slow tempo), 4-5: field, adventure (medium tempo), 6-7: battle, crisis (fast tempo)
- `transp` (*int*) — Transpose in semitones (-5 to +5).
- `instr` (*int*) — Instrumentation (0-3). 0: melody+reverb+bass (3ch), 1: melody+bass+drums (3ch), 2: melody+sub+bass (3ch), 3: melody+sub+bass+drums (4ch).
- `seed` (*int*) — Random seed
- `play` (*bool*) — Play the generated MML. Defaults to False.

**Returns:** `list[str]` — List of MML strings

### Tone & Effect Constants

**Tones:**

- `TONE_TRIANGLE` — 0 — Triangle wave
- `TONE_SQUARE` — 1 — Square wave
- `TONE_PULSE` — 2 — Pulse wave
- `TONE_NOISE` — 3 — Noise

**Effects:**

- `EFFECT_NONE` — 0 — No effect
- `EFFECT_SLIDE` — 1 — Slide
- `EFFECT_VIBRATO` — 2 — Vibrato
- `EFFECT_FADEOUT` — 3 — Fade out
- `EFFECT_HALF_FADEOUT` — 4 — Half fade out
- `EFFECT_QUARTER_FADEOUT` — 5 — Quarter fade out

### Resource Constants

- `NUM_CHANNELS` — 4 — Number of audio channels (0-3)
- `NUM_TONES` — 4 — Number of tone types (0-3)
- `NUM_SOUNDS` — 64 — Number of sounds (0-63)
- `NUM_MUSICS` — 8 — Number of music tracks (0-7)

## Math

### `ceil(x)` — function

Return the smallest integer greater than or equal to x.

**Parameters:**

- `x` (*float*) — Value

**Returns:** `int` — Smallest integer >= x

### `floor(x)` — function

Return the largest integer less than or equal to x.

**Parameters:**

- `x` (*float*) — Value

**Returns:** `int` — Largest integer <= x

### `clamp(x, lower, upper)` — function

Return x clamped between lower and upper.

**Parameters:**

- `x` (*float*) — Value to clamp
- `lower` (*float*) — Minimum value
- `upper` (*float*) — Maximum value

**Returns:** `float` — Clamped value

### `sgn(x)` — function

Return 1 when x is positive, 0 when it is 0, and -1 when it is negative.

**Parameters:**

- `x` (*int/float*) — Value

**Returns:** `int/float` — Sign of the value (1, 0, or -1)

### `sqrt(x)` — function

Return the square root of x.

**Parameters:**

- `x` (*float*) — Value

**Returns:** `float` — Square root of x

### `sin(deg)` — function

Return the sine of deg degrees.

**Parameters:**

- `deg` (*float*) — Angle in degrees

**Returns:** `float` — Sine value

### `cos(deg)` — function

Return the cosine of deg degrees.

**Parameters:**

- `deg` (*float*) — Angle in degrees

**Returns:** `float` — Cosine value

### `atan2(y, x)` — function

Return the arctangent of y/x in degrees.

**Parameters:**

- `y` (*float*) — Y value
- `x` (*float*) — X value

**Returns:** `float` — Angle in degrees

### `rseed(seed)` — function

Set the seed of the random number generator.

**Parameters:**

- `seed` (*int*) — Seed value (non-negative integer)

### `rndi(a, b)` — function

Return a random integer from a to b (inclusive).

**Parameters:**

- `a` (*int*) — Minimum value (inclusive)
- `b` (*int*) — Maximum value (inclusive)

**Returns:** `int` — Random integer from a to b

### `rndf(a, b)` — function

Return a random float from a to b (inclusive).

**Parameters:**

- `a` (*float*) — Minimum value (inclusive)
- `b` (*float*) — Maximum value (inclusive)

**Returns:** `float` — Random float from a to b

### `nseed(seed)` — function

Set the seed of Perlin noise.

**Parameters:**

- `seed` (*int*) — Seed value (non-negative integer)

### `noise(x, y=0, z=0)` — function

Return the Perlin noise value for the specified coordinates.

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate. Defaults to 0.
- `z` (*float*) — Z coordinate. Defaults to 0.

**Returns:** `float` — Perlin noise value

## Font Class

### `Font(filename, font_size=10.0)` — class *(Advanced)*

Create a Font instance from a font file (BDF/OTF/TTF/TTC).

**Parameters:**

- `filename` (*str*) — Font file name (BDF/OTF/TTF/TTC)
- `font_size` (*float*) — Font size. Defaults to 10.0. Not used for BDF.

**Returns:** `Font` — New Font instance

### `Font.text_width(s)` — function *(Advanced)*

Return the display width of the string s in this font.

**Parameters:**

- `s` (*str*) — String to measure

**Returns:** `int` — Width in pixels

## Image Class

### `Image(width, height)` — class *(Advanced)*

Create a new Image instance with the specified size.

**Parameters:**

- `width` (*int*) — Image width
- `height` (*int*) — Image height

**Returns:** `Image` — New Image instance

### `Image.from_image(filename, include_colors=False)` — class *(Advanced)*

Create an Image instance from an image file.

**Parameters:**

- `filename` (*str*) — Image file name
- `include_colors` (*bool*) — Include palette colors from file. Defaults to False.

**Returns:** `Image` — Image instance from file

### `Image.width` — variable

The width of the image.

- **Type:** `int`

### `Image.height` — variable

The height of the image.

- **Type:** `int`

### `Image.set(x, y, data)` — function

Set the image at (x, y) using a list of hex-digit strings. Each character represents a color index (0-f).

**Parameters:**

- `x` (*int*) — X coordinate
- `y` (*int*) — Y coordinate
- `data` (*list[str]*) — Image data as a list of strings

**Example:**

```python
pyxel.images[0].set(10, 10, ["0123", "4567", "89ab", "cdef"])
```

### `Image.load(x, y, filename, include_colors=False)` — function

Load an image file (PNG/GIF/JPEG) at (x, y).

**Parameters:**

- `x` (*int*) — X coordinate
- `y` (*int*) — Y coordinate
- `filename` (*str*) — Image file name (PNG/GIF/JPEG)
- `include_colors` (*bool*) — Include palette colors from file. Defaults to False.

### `Image.pget(x, y)` — function

Get the color of the pixel at (x, y). The coordinates are absolute and not affected by the camera offset.

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate

**Returns:** `int` — Color of the pixel

### `Image.pset(x, y, col)` — function

Draw a pixel with the color col at (x, y).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `col` (*int*) — Color

### `Image.{cls, line, rect, rectb, circ, circb, elli, ellib, tri, trib, fill, blt, bltm, blt3d, bltm3d, text, clip, camera, pal, dither}` — function

Image instances support all drawing functions from the Graphics section. They work the same way but draw on the image instead of the screen.

### `Image.data_ptr()` — function *(Advanced)*

Return the raw data pointer of the image as a ctypes c_uint8 array.

**Returns:** `Any` — Raw data pointer

### `Image.save(filename, scale)` — function *(Advanced)*

Save the image to a file.

**Parameters:**

- `filename` (*str*) — Output file name
- `scale` (*int*) — Scale factor

## Tilemap Class

### `Tilemap(width, height, img)` — class *(Advanced)*

Create a new Tilemap instance.

**Parameters:**

- `width` (*int*) — Tilemap width
- `height` (*int*) — Tilemap height
- `img` (*int/Image*) — Image bank number (0-2) or Image instance

**Returns:** `Tilemap` — New Tilemap instance

### `Tilemap.from_tmx(filename, layer)` — class *(Advanced)*

Create a Tilemap instance from a TMX file.

**Parameters:**

- `filename` (*str*) — TMX file name
- `layer` (*int*) — Layer number (0-)

**Returns:** `Tilemap` — Tilemap instance from TMX file

### `Tilemap.width` — variable

The width of the tilemap.

- **Type:** `int`

### `Tilemap.height` — variable

The height of the tilemap.

- **Type:** `int`

### `Tilemap.imgsrc` — variable

The image bank (0-2) or Image instance referenced by the tilemap.

- **Type:** `int/Image`

### `Tilemap.set(x, y, data)` — function

Set the tilemap at (x, y) using a list of strings. Each tile is a 4-digit hex value representing (image_tx, image_ty), separated by spaces.

**Parameters:**

- `x` (*int*) — X coordinate
- `y` (*int*) — Y coordinate
- `data` (*list[str]*) — Tilemap data as a list of strings

**Example:**

```python
pyxel.tilemaps[0].set(0, 0, ["0000 0100 a0b0", "0001 0101 a1b1"])
```

### `Tilemap.load(x, y, filename, layer)` — function

Load the layer (0-) from the TMX file at (x, y).

**Parameters:**

- `x` (*int*) — X coordinate
- `y` (*int*) — Y coordinate
- `filename` (*str*) — TMX file name
- `layer` (*int*) — Layer number (0-)

### `Tilemap.pget(x, y)` — function

Get the tile at (x, y). The coordinates are absolute and not affected by the camera offset. A tile is a tuple of (image_tx, image_ty).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate

**Returns:** `tuple[int, int]` — (image_tx, image_ty)

### `Tilemap.pset(x, y, tile)` — function

Set a tile at (x, y). A tile is a tuple of (image_tx, image_ty).

**Parameters:**

- `x` (*float*) — X coordinate
- `y` (*float*) — Y coordinate
- `tile` (*tuple[int, int]*) — Tile as (image_tx, image_ty)

### `Tilemap.{cls, line, rect, rectb, circ, circb, elli, ellib, tri, trib, fill, blt, clip, camera}` — function

Tilemap instances support drawing functions from the Graphics section. They work the same way but draw tiles on the tilemap instead of pixels on the screen. Use a tile tuple (image_tx, image_ty) instead of a color value.

### `Tilemap.collide(x, y, w, h, dx, dy, walls)` — function

Resolve collisions after applying pixel movement (dx, dy) to the pixel rectangle at (x, y) with pixel size (w, h), and return the adjusted (dx, dy). walls is a list of tile coordinates that act as obstacles.

**Parameters:**

- `x` (*float*) — Rectangle X position
- `y` (*float*) — Rectangle Y position
- `w` (*float*) — Rectangle width
- `h` (*float*) — Rectangle height
- `dx` (*float*) — Movement in X
- `dy` (*float*) — Movement in Y
- `walls` (*list[tuple[int, int]]*) — List of wall tiles (image_tx, image_ty)

**Returns:** `tuple[float, float]` — Adjusted (dx, dy)

### `Tilemap.data_ptr()` — function *(Advanced)*

Return the raw data pointer of the tilemap as a ctypes c_uint16 array (4 bytes per tile: image_tx, image_ty).

**Returns:** `Any` — Raw data pointer

## Sound Class

### `Sound()` — class *(Advanced)*

Create a new Sound instance.

**Returns:** `Sound` — New Sound instance

### `Sound.notes` — variable

List of notes (0-59). Higher values produce higher pitches. 33 = 'A2' (440 Hz). Rests are -1.

- **Type:** `list[int]`

### `Sound.tones` — variable

List of tones (0: Triangle, 1: Square, 2: Pulse, 3: Noise).

- **Type:** `list[int]`

### `Sound.volumes` — variable

List of volumes (0-7).

- **Type:** `list[int]`

### `Sound.effects` — variable

List of effects (0: None, 1: Slide, 2: Vibrato, 3: FadeOut, 4: Half-FadeOut, 5: Quarter-FadeOut).

- **Type:** `list[int]`

### `Sound.speed` — variable

Playback speed. 1 is the fastest, and the larger the number, the slower the playback speed. At 120, one note equals 1 second.

- **Type:** `int`

### `Sound.set(notes, tones, volumes, effects, speed)` — function

Set notes, tones, volumes, and effects using strings. If the tones, volumes, or effects string is shorter than the notes, it repeats from the beginning.

**Parameters:**

- `notes` (*str*) — Note string
- `tones` (*str*) — Tone string
- `volumes` (*str*) — Volume string
- `effects` (*str*) — Effect string
- `speed` (*int*) — Playback speed

### `Sound.set_notes(notes)` — function

Set the notes using a string made of note names (CDEFGAB), optional sharp (#) or flat (-), octave (0-4), and rests (R). Case-insensitive, whitespace is ignored.

**Parameters:**

- `notes` (*str*) — Notes string

**Example:**

```python
pyxel.sounds[0].set_notes("g2b-2d3r rf3f3f3")
```

### `Sound.set_tones(tones)` — function

Set the tones with a string of TSPN (or 0-9 for custom tone numbers). Case-insensitive, whitespace is ignored.

**Parameters:**

- `tones` (*str*) — Tones string

**Example:**

```python
pyxel.sounds[0].set_tones("ttss pppn")
```

### `Sound.set_volumes(volumes)` — function

Set the volumes with a string of 01234567. Whitespace is ignored.

**Parameters:**

- `volumes` (*str*) — Volumes string

**Example:**

```python
pyxel.sounds[0].set_volumes("7777 7531")
```

### `Sound.set_effects(effects)` — function

Set the effects with a string of NSVFHQ. Case-insensitive, whitespace is ignored.

**Parameters:**

- `effects` (*str*) — Effects string

**Example:**

```python
pyxel.sounds[0].set_effects("nfnf nvvs")
```

### `Sound.mml(code)` — function

Switch to MML mode with the given MML string. In MML mode, normal parameters such as notes and speed are ignored. For available MML commands, see the Pyxel MML Commands page.

**Parameters:**

- `code` (*str*) — MML string

**Example:**

```python
pyxel.sounds[0].mml("T120 Q90 @1 V100 O5 L8 C4&C<G16R16>C.")
```

### `Sound.mml()` — function

Exit MML mode and return to normal mode.

### `Sound.pcm(filename)` — function

Load an audio file (WAV/OGG) for playback.

**Parameters:**

- `filename` (*str*) — Audio file name (WAV/OGG)

**Example:**

```python
pyxel.sounds[0].pcm("sounds/bgm.ogg")
```

### `Sound.pcm()` — function

Exit PCM mode and return to normal mode.

### `Sound.save(filename, sec, ffmpeg=False)` — function *(Advanced)*

Create a WAV file from the sound with the specified duration in seconds.

**Parameters:**

- `filename` (*str*) — Output WAV file name
- `sec` (*float*) — Duration in seconds
- `ffmpeg` (*bool*) — Also create MP4 file (requires FFmpeg). Defaults to False.

### `Sound.total_sec()` — function *(Advanced)*

Return the playback time in seconds. Return None for infinite loops.

**Returns:** `float/None` — Playback time in seconds, or None for infinite loops

## Music Class

### `Music()` — class *(Advanced)*

Create a new Music instance.

**Returns:** `Music` — New Music instance

### `Music.seqs` — variable

A two-dimensional list of sounds (0-63) across multiple channels.

- **Type:** `list[list[int]]`

### `Music.set(seq0, seq1, seq2, ...)` — function

Set the lists of sounds (0-63) for each channel. An empty list means the channel is not used.

**Parameters:**

- `seq0, seq1, ...` (*list[int]*) — Sound lists for each channel. Empty list = unused.

**Example:**

```python
pyxel.musics[0].set([0, 1], [], [3])
```

### `Music.save(filename, sec, ffmpeg=False)` — function *(Advanced)*

Create a WAV file from the music with the specified duration in seconds.

**Parameters:**

- `filename` (*str*) — Output WAV file name
- `sec` (*float*) — Duration in seconds
- `ffmpeg` (*bool*) — Also create MP4 file (requires FFmpeg). Defaults to False.

## Channel Class

### `Channel()` — class *(Advanced)*

Create a new Channel instance.

**Returns:** `Channel` — New Channel instance

### `Channel.gain` — variable *(Advanced)*

The gain (volume) of the channel. Defaults to 0.125.

- **Type:** `float`

### `Channel.detune` — variable *(Advanced)*

Pitch detune in cents (100 cents = 1 semitone). Defaults to 0.

- **Type:** `int`

### `Channel.play(snd, sec=0, loop=False, resume=False)` — function *(Advanced)*

Play the sound snd on this channel. snd can be a sound number, a list, a Sound instance, a list of Sounds, or an MML string.

**Parameters:**

- `snd` (*int/list/Sound/str*) — Sound number (0-63), list of numbers, Sound instance, list of Sounds, or MML string
- `sec` (*float*) — Playback start position in seconds. Defaults to 0.
- `loop` (*bool*) — Loop playback. Defaults to False.
- `resume` (*bool*) — Resume previous sound after playback ends. Defaults to False.

### `Channel.stop()` — function *(Advanced)*

Stop playback on this channel.

### `Channel.play_pos()` — function *(Advanced)*

Get the playback position as a tuple of (sound_index, sec). Return None when playback has stopped.

**Returns:** `tuple[int, float]/None` — (sound_index, sec) or None

## Tone Class

### `Tone()` — class *(Advanced)*

Create a new Tone instance.

**Returns:** `Tone` — New Tone instance

### `Tone.mode` — variable *(Advanced)*

Tone mode (0: Wavetable, 1: ShortPeriodNoise, 2: LongPeriodNoise).

- **Type:** `int`

### `Tone.sample_bits` — variable *(Advanced)*

Sample bits for the wavetable (1-16). Defaults to 4.

- **Type:** `int`

### `Tone.wavetable` — variable *(Advanced)*

Wavetable data as a list of sample values. Each value must be in range 0 to (2^sample_bits - 1).

- **Type:** `list[int]`

### `Tone.gain` — variable *(Advanced)*

Tone gain. Defaults to 1.0.

- **Type:** `float`

---

### Coding Policy Audit (`docs/coding-policy-audit.md`)

# Coding Policy Audit Procedure

This file defines how to run an exhaustive audit against
`docs/coding-policy.md`. It does not define coding standards, add exceptions, or
weaken the policy. If this procedure and the policy disagree, the policy wins
and this procedure must be corrected before the audit is accepted.

An auditor reads both files in this order:

- `docs/coding-policy.md`: the source of truth for standards, scope, and
  examples.
- `docs/coding-policy-audit.md`: the execution protocol used to prove that the
  policy was checked completely and evenly.

## Operating Rules

- No sampling. Every in-scope file, normative criterion, applicable subject,
  cross-file relation, hot-path surface, and required verification gate is
  represented by an artifact row.

- No cherry-picking. A local fix is incomplete until every comparable subject
  and affected relation is checked to the same depth.

- No unstated memory. Previous conversations, old audit notes, prior summaries,
  and repository prevalence may suggest probes, but they are not evidence until
  rechecked against the frozen target and recorded in the current artifacts.

- No majority rule. Explicit policy determines departures from the relevant
  language's established idiom; otherwise that idiom determines the correct
  form. Existing repetition is used only to discover related subjects and uneven
  application.

- No example criteria. An example illustrates its parent rule and is recorded
  as example coverage; matching or differing from the example alone never
  determines a verdict.

- No summary-only evidence. Counts, search results, formatter success, test
  success, and reviewer summaries do not prove a check unless its row names what
  was inspected and why the verdict follows.

- Use precise result labels. A run is an exhaustive audit only when the
  Completion Gate is satisfied. Until then, or when the requested scope is
  intentionally narrower, report the work as a targeted review, fix pass, gate
  check, or pending audit according to the artifacts actually produced.

- The worktree under review is the audit target. Start from `git ls-files`, add
  intended untracked files, and apply the policy's Scope section to that combined
  set.

- The frozen target is immutable within a run. If its files, intended additions,
  policy, or procedure change, preserve the run and start another. Regenerate an
  artifact when only one of its recorded in-run inputs changes; a stale row is
  `pending`, never reused as proof. The final report names every preserved run
  that led to the completed one.

## When to Run

Run this procedure:

- when an exhaustive coding-policy audit is explicitly requested;
- before a release tag, as part of the release checklist;
- after a substantive revision of `docs/coding-policy.md`, on every subject
  whose verdict may change because of the revision;
- after a substantive revision of this procedure, on this procedure and every
  affected audit artifact or subject.

## Artifact Rules

Each run writes artifacts to one directory named in the final report. Every TSV
has a header, uses one row per declared unit, and has the declared column count
on every row. Text artifacts use UTF-8 without a BOM, LF line endings, and one
terminal LF. JSON objects use the declared key order and no insignificant
whitespace. TSV fields contain no literal tabs or newlines. Lists are compact
JSON arrays with no duplicate logical entries; an aligned value list retains one
value per key even when values are equal. Reference lists preserve the defining
artifact's row order, enum lists preserve this procedure's declaration order,
and other lists are sorted bytewise unless a recorded source command defines
their order.

Path values encode repository-relative Git path bytes: `/` and ASCII letters,
digits, `.`, `_`, and `-` remain literal; every other byte is `%HH` with uppercase
hex digits. Plural TSV fields are JSON arrays unless their schema says otherwise;
`policy_line` is one policy source reference and `policy_lines` is an array of
them. Artifact rows are sorted bytewise by their identifier.

Every data row has the explicit identifier declared by its artifact. Except for
the separately defined file and artifact IDs, an ID is its column name without
`_id`, followed by `-` and the lowercase SHA-256 of its natural-key JSON array.
That array uses the key values below in listed order, with no whitespace, only
required JSON escapes, and literal non-ASCII characters. Each
verdict-independent row ID is assigned before that row's verdict; finding and
review IDs are computed when those rows are created. IDs use the path-encoding
alphabet and never contain `:`.

Completeness of verdict-independent sets is validated from natural keys, never
from IDs or observed verdicts; post-verdict sets follow their declared
derivations. Natural keys are: policy source lines and normalized rule text for
criteria; policy line for policy coverage; artifact path for the manifest;
`path` for target files and scope; kind and family for relations; family for hot
paths; `cwd`, command, and trigger for commands; criterion and subject for
applicability and checks; source and target references plus finding text for
findings; criterion and subject family for balance rows; and review kind and
subject key for independent-review rows.
Missing or duplicate identifiers or natural keys, unknown criteria or subjects,
invalid enum values, empty required fields, and column-count errors are artifact
failures.

An evidence reference is one of:

- `source:<path>:<line>` or `source:<path>:<first>-<last>`;
- `source:<path>` for a whole-file check;
- `row:<artifact>:<row_id>` for a TSV row;
- `artifact:<artifact>`, optionally followed by `:<line>` or
  `:<first>-<last>`, for whole-artifact or raw-line evidence.

Source lines are one-based and ranges are inclusive. Paths use the encoding above;
`artifact` is a required artifact filename. A deleted path is cited through its
hunk in `current-diff.patch`, not as current source.

Every reference resolves to exactly one current source location, artifact
location, or row. The evidence graph is acyclic and terminates in current source,
a frozen-target root artifact, or a successful target-bound command row. Broken,
cyclic, or unterminated evidence blocks completion.

`manifest.tsv` binds manifested artifacts to their exact inputs and the frozen
target. A manifested artifact is stale when its current hash, input hashes, or
`target_sha256` differs from the manifest. The independent review is stale when
its target or manifest hash differs; the summary is stale when any of its three
hashes differs. Immediately before completion, the target snapshot and diff are
independently re-derived from the live worktree; comparing stored artifacts with
one another is insufficient. No artifact hash, fingerprint, or verdict is an
input to its own computation. Identifiers only locate units derived from natural
keys; an observed identifier set never proves its own completeness.

## Required Artifacts

- `target_files.tsv`
  - Columns: `file_id`, `path`, `source`, `git_mode`, `content_sha256`.
  - Contains every tracked path not deleted by the frozen diff, plus every
    intended untracked file, whether the policy later includes or excludes it.
    A missing sparse- or skip-worktree path blocks the freeze rather than being
    treated as a deletion. `source` is `tracked` or `untracked_intended`.
  - `file_id` equals the encoded `path`. `git_mode` is the six-digit mode the
    frozen target tree would record. `content_sha256` hashes the working-tree
    bytes, the link-target bytes for a symlink, or the hexadecimal bytes of the
    recorded commit ID for a gitlink.

- `freeze.json`
  - Contains exactly: `schema_version`, `branch`, `base_commit`,
    `head_commit`, `policy_sha256`, `procedure_sha256`,
    `target_files_sha256`, `diff_sha256`, `target_sha256`, and `artifact_dir`.
  - `schema_version` is the integer `1` for this schema and increments whenever a
    required artifact's fields or meaning change. `branch` is the short branch
    name using the path-byte encoding above, or null; commits are full object
    IDs; digests are lowercase SHA-256 strings; and `artifact_dir` is the absolute
    run-directory path.
  - `base_commit` is the previous release commit for a release audit; otherwise
    it is the explicitly requested comparison base, the merge base of `HEAD` and
    its configured upstream when no base was requested, or `HEAD` when no
    upstream exists.
  - `target_sha256` is the SHA-256 of a UTF-8 JSON array containing, in order,
    `schema_version`, `branch`, `base_commit`, `head_commit`, `policy_sha256`,
    `procedure_sha256`, and `target_files_sha256`. The serialization uses no
    whitespace and only required JSON escapes.
  - The object records the target before any verdict is written.

- `current-diff.patch`
  - Contains the exact diff against `base_commit`, including intended untracked
    files as added-file patches, in Git's binary-capable full-index format.
    The tracked diff comes first; added-file patches follow in encoded-path order.
  - Its SHA-256 equals `freeze.json.diff_sha256`.

- `manifest.tsv`
  - Columns: `artifact_id`, `path`, `sha256`, `input_artifact_ids`,
    `input_sha256s`, `target_sha256`.
  - Contains one row for every required artifact except itself,
    `independent_review.tsv`, and `audit_summary.json`.
  - `artifact_id` is the artifact filename and `path` is its path relative to the
    run directory.
  - Input ID and hash arrays have equal length and preserve dependency order.
  - The dependency graph is acyclic and contains these direct edges:
    - `target_files.tsv` and `current-diff.patch` are roots;
    - `freeze.json` depends on both roots;
    - `criteria.tsv` and `policy_coverage.tsv` depend on `freeze.json`;
    - `scope.tsv` depends on `target_files.tsv`, `criteria.tsv`, and
      `policy_coverage.tsv`;
    - `relations.tsv`, `hot_paths.tsv`, and `commands.tsv` depend on
      `criteria.tsv` and `scope.tsv`, with change-driven inventories also
      depending on `current-diff.patch`;
    - `applicability.tsv` depends on `criteria.tsv`, `scope.tsv`, `relations.tsv`,
      `hot_paths.tsv`, and `commands.tsv`;
    - `checks.tsv` depends on `applicability.tsv` and `commands.tsv`;
    - `coverage_balance.tsv` depends on `criteria.tsv`, `applicability.tsv`, and
      `checks.tsv`;
    - `findings.tsv` depends on `checks.tsv` and `coverage_balance.tsv`.
  - Each row also names every additional direct artifact input it actually uses.

- `criteria.tsv`
  - Columns: `criterion_id`, `policy_lines`, `subject_types`,
    `surface_kinds`, `file_selector`, `rule_text`.
  - Contains one row for each top-level rule bullet and each normative
    introductory paragraph in `docs/coding-policy.md`.
  - Nested authoritative enumerations belong to their parent criterion.
    `e.g.` bullets and hypothetical anti-patterns never create criteria.
  - `subject_types` is a non-empty JSON array drawn from `file`, `relation`,
    `hot_path`, `process`, and `command`.
  - `file` governs a file surface; `relation` compares files; `hot_path` governs
    a policy-listed performance family; `process` governs a repository-wide or
    audit obligation with no narrower subject; and `command` requires execution.
    Every applicable type is listed.
  - `surface_kinds` is a JSON array used only by `file` criteria and drawn
    from `path`, `source`, `test`, `prose`, `translation`,
    `release_notes`, `policy`, `configuration`, and `structured_data`.
    It is non-empty exactly when `subject_types` contains `file`.
  - `file_selector` is empty for a non-file criterion. Otherwise it states the
    rule-derived predicate that selects governed content within the candidate
    surface kinds. It is `all` only where the rule governs every candidate file;
    examples and hand-picked repository paths cannot define it.
  - `rule_text` is the normative text with internal whitespace collapsed to one
    ASCII space. Any text change changes the criterion ID; a semantic addition,
    removal, split, or merge changes the criterion set.

- `policy_coverage.tsv`
  - Columns: `coverage_id`, `policy_line`, `line_text`, `coverage_kind`,
    `criterion_ids`, `rationale`.
  - Contains exactly one row for every non-blank policy line; `line_text` is the
    exact line content.
  - `coverage_kind` is `criterion`, `example`, or `structural`.
    Criterion and example rows name their parent criteria. Structural rows have
    an empty `criterion_ids` array and explain why the line imposes no
    requirement.

- `scope.tsv`
  - Columns: `file_id`, `path`, `source`, `scope_status`,
    `surface_kinds`, `reason`, `policy_lines`.
  - Contains exactly the files in `target_files.tsv`, with the same file IDs,
    paths, and sources.
  - `source` is `tracked` or `untracked_intended`; `scope_status` is
    `included` or `excluded`.
  - Every included file's `surface_kinds` contains `path` plus every applicable
    kind. Excluded files have an empty surface list. Every row cites the governing
    policy lines and explains its inclusion or exclusion.
  - `path` applies to every included file; `source` to syntax, structure,
    identifiers, comments, or other non-prose content; `test` to verification
    code or data; `prose` to documentation, descriptive metadata, or user-facing
    natural language other than code comments; `translation` to content with
    language variants; `release_notes` to changelog entries; `policy` to these
    two policy documents; `configuration` to configuration content; and
    `structured_data` to machine-parsed non-code data. Kinds overlap and follow
    content and role, not filename extension alone.

- `relations.tsv`
  - Columns: `relation_id`, `relation_kind`, `family`, `member_file_ids`,
    `criterion_ids`, `policy_lines`, `rationale`, `derivation_refs`.
  - Expands every cross-file concern before checking begins. A file may belong
    to any number of relations.
  - `member_file_ids` contains unique IDs from included `scope.tsv` rows. It is
    empty only for a policy-mandated family with no current member, proved by
    `derivation_refs`.
  - `relation_kind` is `sibling`, `mirror`, `translation`, `exception`, or
    `change_dependency`.
  - `criterion_ids` contains every relation criterion governing the family;
    `policy_lines` cites those rules.
  - Required relations include applicable naming and error-message families,
    API/binding/stub mirrors, translated content, policy exception groups,
    release notes against code changes, and tests against changed behavior.
  - A change-dependency relation cites deleted or renamed inputs through the
    frozen diff; its member IDs still name only current included files.
  - An exception relation cites the exact policy lines, convention, and reason.
    Repository prevalence is not a rationale.

- `hot_paths.tsv`
  - Columns: `hot_path_id`, `family`, `member_file_ids`, `entry_points`,
    `criterion_ids`, `cost_risks`, `derivation`, `derivation_refs`.
  - Contains exactly one row for every policy-listed hot-path family and
    aggregates every implementation surface in that row.
  - `criterion_ids` contains every hot-path criterion governing the family.
  - `member_file_ids` contains unique IDs from included scope rows and is empty
    only in an absence row.
  - `cost_risks` names every concrete cost implied by the rule and inspected
    implementation; it is not limited to examples or a fixed category list.
  - `entry_points` and `derivation_refs` contain evidence references;
    `derivation` explains how they establish completeness.
  - A family with no implementation surface has an explicit absence row with
    repository-wide derivation evidence.

- `commands.tsv`
  - Columns: `command_id`, `criterion_ids`, `cwd`, `command`, `trigger`,
    `target_sha256`, `exit_status`, `key_output`.
  - Declares every required and targeted command before it runs.
  - `cwd` is `.` or an encoded repository-relative directory; `command` records
    the exact non-interactive invocation.
  - `criterion_ids` contains every command criterion the invocation checks. It
    may be empty only for a procedure-level validation command; such a command
    remains subject to its trigger and the Completion Gate.
  - `exit_status` is a non-negative integer or `not_run`; zero passes and any
    other integer fails. A `not_run` row records the blocker and cannot clear a
    command criterion.
  - A successful command is evidence only when its target digest matches
    `freeze.json.target_sha256`.

- `applicability.tsv`
  - Columns: `check_id`, `criterion_id`, `subject_ref`,
    `derivation_refs`.
  - Contains the complete expected check set, derived before verdicts.
  - `subject_ref` is `file:<file_id>`, `relation:<relation_id>`,
    `routing:<criterion_id>`, `hot_path:<hot_path_id>`,
    `process:<criterion_id>`, `command:<command_id>`, or
    `absence:<criterion_id>:<subject_type>`.
  - File candidates are the intersections of criterion and included-file surface
    kinds. The selector produces the file checks from those candidates, and every
    file criterion also produces one routing check whose derivation proves that
    the selector was applied exhaustively. Relation, hot-path, and command checks
    come from the corresponding `criterion_ids`; each process criterion produces
    one process check.
  - Each declared non-file subject type with no concrete subject produces one
    absence check. Its derivation proves the absence; it passes only when absence
    satisfies the rule. A criterion missing any required applicability row, or a
    row not produced by these rules, is an artifact failure.

- `checks.tsv`
  - Columns: `check_id`, `criterion_id`, `subject_ref`, `verdict`,
    `evidence`, `evidence_refs`.
  - Contains exactly one row for every `applicability.tsv` row, with the same
    ID, criterion, and subject.
  - A local check does not replace a relation or hot-path check, and one
    subject's pass does not override another subject's non-pass.
  - A routing check proves selection completeness only; it never proves that a
    selected file satisfies the criterion.

- `findings.tsv`
  - Columns: `finding_id`, `source_ref`, `severity`, `target_ref`,
    `criterion_ids`, `finding`, `proposed_fix`.
  - Contains one or more rows for every current `fix` verdict in `checks.tsv` or
    `coverage_balance.tsv`, one row per distinct target defect, and no rows from
    any other verdict.
  - `source_ref` names the originating fix row; `target_ref` names the defective
    source or artifact; and `criterion_ids` includes the source criterion and any
    other directly violated criterion.
  - `severity` is `blocker`, `high`, `medium`, or `low`; severity never
    changes whether a policy finding must be resolved.

- `coverage_balance.tsv`
  - Columns: `balance_id`, `criterion_id`, `subject_family`, `check_ids`,
    `comparator_balance_ids`, `verdict`, `rationale`, `evidence_refs`.
  - Contains one row for every criterion and subject family represented in
    `applicability.tsv`. Subject families are `file:<surface_kind>`,
    `routing`, `relation:<relation_kind>`, `hot_path:<family>`, `process`, and
    `command`, plus `absence:<subject_type>`.
  - `verdict` is `pass`, `fix`, `review`, or `pending`.
  - Comparators are other families for the same criterion and structurally
    analogous criterion-family rows. The comparator list is empty only when no
    such row exists and the rationale proves why.
  - Fix counts need not match. A zero-fix family passes only when its
    checks and evidence show depth comparable to its named comparator rows.

- `independent_review.tsv`
  - Columns: `review_id`, `reviewer`, `review_kind`, `subject_key`, `source_ref`,
    `target_sha256`, `manifest_sha256`, `verdict`, `evidence_refs`.
  - `review_kind` is `expected_key`, `check`, `balance`, or `artifact`;
    `subject_key` is a compact JSON array beginning with the artifact filename,
    followed by that unit's natural-key values. The whole-manifest key is
    `["manifest.tsv"]`; each manifest-row key adds its artifact path.
  - Contains one row for every independently expected natural key in target
    files, policy extraction, scope, subject inventories, and applicability; one
    row for every check and balance row; and artifact rows for the manifest as a
    whole and for every manifest row.
  - `source_ref` resolves to the corresponding current row or artifact. It is
    empty only when an expected unit is missing, which requires a non-pass
    verdict.
  - Every row names the independent reviewer and the reviewed target and manifest
    hashes. `verdict` is `pass`, `fix`, or `pending`.

- `audit_summary.json`
  - Contains exactly: `schema_version`, `target_sha256`, `manifest_sha256`,
    `review_sha256`, `expected_checks`, `actual_checks`, `verdict_counts`,
    `finding_counts`, `command_counts`, `balance_counts`, `review_counts`,
    `validation_errors`, `stale_artifacts`, and `completion_status`.
  - `schema_version` and `target_sha256` equal the freeze; `manifest_sha256` and
    `review_sha256` are the lowercase SHA-256 hashes of the current files.
  - `verdict_counts` has exactly `pass`, `fix`, `review`, and `pending`;
    `finding_counts` has `blocker`, `high`, `medium`, and `low`;
    `command_counts` has `passed`, `failed`, and `not_run`;
    `balance_counts` has `pass`, `fix`, `review`, and `pending`;
    and `review_counts` has `pass`, `fix`, and `pending`.
  - `validation_errors` has exactly `schema`, `identifier`, `enum`, `reference`,
    `expected_key`, `dependency`, `evidence_cycle`, and
    `evidence_termination`.
  - `expected_checks`, `actual_checks`, every count member, and every validation
    member are non-negative integers. `stale_artifacts` is an array of artifact
    IDs.
  - `completion_status` is `complete` or `pending` and is derived from the
    source artifacts; it never overrides them.

## Verdicts and Evidence

Every `checks.tsv.verdict` is one of:

- `pass`: the governed check obligation is satisfied, with substantive current
  evidence;
- `fix`: the governed policy or procedure obligation is violated;
- `review`: semantic judgment is required;
- `pending`: required evidence, execution, or review is incomplete.

Coverage-balance verdicts use the same four meanings for their audit obligation.

Do not use `not applicable`. Applicability is decided before verdicts, and only
applicable criterion/subject pairs enter `applicability.tsv`.

Evidence is substantive only when it:

- names the exact subject and surface inspected;
- explains why the verdict follows from the rule rather than an example;
- records measured or mechanical support where the rule requires it;
- names peers or dependencies for relation verdicts;
- avoids bare phrases such as "no issue", "checked", or "matches policy".

Search output, inventories, formatter success, test success, finding counts, and
reviewer summaries are probes or command evidence, not findings by themselves. A
finding identifies the current source or artifact row, criterion, actionable
defect, and relevant peer or dependency evidence.

## Phases

Run phases in order. Missing expected rows, invalid references, stale artifacts,
or schema errors block the current phase. Non-pass verdicts proceed through
balance and internal resolution, but block independent review and final
validation until resolved.

1. Freeze the target.
   - Record every target file's mode and content digest, the branch, base and
     head commits, policy and procedure digests, artifact directory, and exact
     diff.
   - Write `target_files.tsv`, `freeze.json`, and `current-diff.patch` before
     verdicts.

2. Extract the policy.
   - Build `criteria.tsv` from normative units only.
   - Build `policy_coverage.tsv`, classifying every non-blank line as criterion,
     example, or structural.

3. Build scope.
   - Classify every target snapshot row.
   - Apply policy exclusions and assign all applicable surface kinds.

4. Build subject inventories.
   - Expand all cross-file relations.
   - Enumerate every hot-path surface.
   - Declare required and targeted commands.

5. Derive applicability.
   - Generate `applicability.tsv` from criteria and subject inventories.
   - Validate the complete expected check set before writing verdicts.

6. Perform checks and run commands.
   - Inspect every applicable subject and write one `checks.tsv` row per
     expected check.
   - Inspect source directly; searches and automation remain probes.
   - Execute every triggered command and record its target-bound result.
   - Run `make format` after code or formatter-managed document changes.
   - Run `make lint` and `make lint-wasm` in every exhaustive run.
   - Run `make test` after code changes.
   - Run targeted parsers and validators for changed structured files, applicable
     prose or documentation tests, and `git diff --check`.
   - If a command changes the frozen target, preserve the run and start another.

7. Check audit balance.
   - Compare each criterion-family row with every comparator it names.
   - Re-run the same probes on an unexplained zero-fix family and keep the
     balance row `pending` until the difference is justified.

8. Resolve reviews and record findings.
   - Resolve every `review` as `pass` or `fix` from current evidence, without
     inventing exceptions. Keep missing evidence or blocked execution `pending`.
   - Generate `findings.tsv` from the resulting `fix` rows.
   - Correct an audit-evidence defect in place and regenerate every dependent
     artifact. To correct the frozen target, preserve the current pending run and
     start a new run against the changed target.
   - Repeat the affected phases until every check and balance row is `pass` and
     `findings.tsv` is empty.

9. Run independent review.
    - A reviewer who produced none of the audited artifacts independently derives
      policy, inventory, and applicability natural keys from the frozen source
      before opening the run's verdicts or findings.
    - Give that reviewer both documents, the frozen target, complete inventories,
      applicability, checks, findings, balance evidence, and manifest for
      comparison and source review. Record every required review row in
      `independent_review.tsv`.
    - The reviewer validates every expected key, verdict, and evidence chain;
      review sampling cannot clear this gate.
    - Every actionable review result is `fix` regardless of severity. Propagate
      source defects to checks and findings, and artifact defects to validation
      errors.
    - Correct audit evidence in place; for a target correction, preserve the run
      and start another. Repeat review after either change.
    - If independent review is unavailable, completion remains `pending`.

10. Validate completion.
    - Validate every schema, enum, identifier, natural key, reference, dependency,
      evidence chain, count, and manifest hash from its source artifact.
    - Re-derive the target file snapshot and diff from the live worktree and
      require exact equality with the freeze.
    - Require the independent-review set to be exact, every review verdict to be
      `pass`, and every row to name the current target and manifest hashes.
    - Write `audit_summary.json` only after the independent review is clean.

## Delegation Rules

When an audit is split, the lead auditor gives each auditor both documents, the
frozen target, complete subject inventories, exact assigned check IDs, and all
dependencies needed by those checks.

Delegated summaries are not evidence. The lead reads every returned row,
validates its references, rejects copied or shallow evidence, and resolves
conflicting verdicts by re-reading the subjects.

## Completion Gate

The audit is complete only when:

- the live target snapshot and diff exactly match the freeze;
- every required artifact exists, every manifested artifact matches its schema
  and manifest inputs, and every artifact is bound to the frozen target;
- policy coverage, scope, subject inventories, and applicability contain exactly
  their independently derived expected natural keys;
- `checks.tsv` contains exactly the applicability set and every verdict is
  `pass`;
- every `coverage_balance.tsv` verdict is `pass`;
- `findings.tsv` is empty;
- `independent_review.tsv` contains exactly its independently derived expected
  natural keys, every verdict is `pass`, and its target and manifest hashes are
  current;
- every evidence reference resolves, the evidence graph is acyclic, and every
  chain terminates in current source, a frozen-target root artifact, or successful
  target-bound command evidence;
- every triggered command succeeded against the frozen target;
- `audit_summary.json` matches the current target, manifest, and review hashes,
  reports matching check counts, zero validation errors, zero stale artifacts, and
  `completion_status: complete`.

Do not claim completion before every condition is proved. If any condition is
uncertain, the verdict is `pending`.

---

### Coding Policy (`docs/coding-policy.md`)

# Pyxel Coding Policy

## Principles

- **Determinism.** A rule produces the same verdict on the same frozen input regardless of who applies it or when. Taste alone never determines a verdict.
- **Self-applicability.** Every rule applies to every in-scope surface it governs (see Verification > Scope), including the corresponding parts of this document.
- **Performance first.** Subject to correctness, demonstrated hot-path cost overrides language conventions and idiomatic style.
- **Cross-file consistency.** Explicit policy determines required departures from language idiom; otherwise language idiom determines the correct form, which comparable sites use uniformly.
- **Read naturally.** Code reads concisely to a fluent reader of its language; the language's idioms are preferred over invented forms.

## Standards

### Source Code

#### Performance

- Code on the hot paths eliminates avoidable cost. The hot paths are:
  - per-pixel blit and primitive draws (line, circle, rect);
  - per-pixel 3D rasterization (triangle fill and shading);
  - per-sample voice synthesis;
  - per-frame voice update for MML and BGM;
  - per-frame 3D collision and BVH queries;
  - the PyO3 FFI boundary (argument marshaling and return paths);
  - SIMD or multi-threaded sections.

- On hot paths, idiomatic patterns with a measured or mechanically demonstrated avoidable cost are not used.
  - e.g., per-frame heap allocations (`Vec::new`, `format!`, `Box::new` in inner loops); avoidable copies or type conversions; bounds checks in tight loops; missed SIMD, loop-unrolling, or inlining opportunities.

- Outside hot paths, code stays idiomatic and readable. Micro-optimization is reserved for the listed hot paths.
  - e.g., `for x in xs { f(x) }` in a config loader is preferred over a hand-unrolled alternative.

#### Naming

- Mechanical naming rules (the language's standard case conventions and lint-enforced patterns) apply first.

- File and directory names use a policy-compliant, language-idiomatic base-name, separator, and role-suffix pattern uniformly within their sibling group. Public URLs, generated paths, and author-titled assets keep their established spelling unless every mirrored reference is renamed together.
  - e.g., `*_wrapper.rs` files keep the wrapper suffix, `web/*/index.html` keeps the route directory as the page identity, and `laser-jetman.html` keeps its author-chosen hyphenation.

- A symbol referenced from more than one file uses the same base name at every site (function and type names, CSS classes, HTML IDs, i18n keys, public API entries). Suffixed variants of the base name are allowed when each variant is exposed as a separate public entry. When mirrors disagree, the authoritative public surface wins; between a binding and the `.pyi`, the `.pyi` wins. If no authoritative surface exists, the language-idiomatic name is chosen and every mirrored reference is updated together.
  - e.g., a `pyxel-core` function `gen_bgm` keeps the same base name in `crates/pyxel-binding/src/*_wrapper.rs` and `python/pyxel/__init__.pyi`; if it is split for separate exposure, the split uses suffixes (`gen_bgm_mml`, `gen_bgm_json`) rather than a renaming.

- A name is rewritten when it gives one concept a different base from its peers, breaks a symmetric verb family, repeats its owner or type without distinguishing meaning, or names an obsolete concept.
  - e.g., `titleBlock` and `titleDiv` for the same UI concept — anti-pattern; `Canvas.drawCanvas()` — anti-pattern (stutter).

- A language's idiomatic abbreviations are kept as-is.
  - e.g., Python and Rust use `i` for a loop counter and `e` for an exception variable; JavaScript uses `e` for an event and `el` for a DOM element.

- A locally reasonable name with no peer to harmonize with is left as-is. The rename rule above applies when peers exist; with no peer, taste alone is not grounds for renaming.
  - e.g., a self-contained file using `titleDiv` stays as it is when no comparable sibling exists; the same name in a file with sibling files using `titleBlock` is renamed to match.

#### Ordering

- Definitions are ordered top-down: high-level structures and public types come before the free functions that consume them.
  - e.g., a Rust file places `pub struct Foo { ... }` and its impls before any free function consuming `Foo`.

- Where the language requires forward declarations, they precede their use, overriding top-down ordering at the local level.

- Configuration files follow each format's idiomatic grouping; within each group, entries are sorted alphabetically unless the format itself prescribes another order.
  - e.g., `Cargo.toml` keeps dependencies, build dependencies, features, and release profiles in their conventional tables and sorts entries within each table.

#### Comments

- Every comment is in English.

- A comment exists only when it adds intent that neither the code nor an existing comment shows; a shared rationale lives once, at the site that owns the decision, unless another rule requires the repetition. Required cases are mechanical or non-obvious operations (bit-twiddling, format-specific encoding) and non-local invariants.
  - e.g., `i += 1  # increment i` — anti-pattern; `i += 1  # wrap at frame boundary` — typical; the same workaround explained at its deciding and dependent sites — anti-pattern.

- A comment is as short as its intent allows; comments expressing equally complex intent use comparable granularity.
  - e.g., a five-line header on a short helper where sibling files use one-line headers — anti-pattern (surplus wording); a widget's `# Variables:` and `# Events:` blocks matching the convention across widget files — typical.

- A one-line separator comment identifies a meaningful group only when the language's structure does not make the group equally clear; no size threshold alone requires one. It uses the language's idiomatic single-line comment form, in sentence case, without decorative dashes or banners.
  - e.g., Python `# Event handlers`, Rust `// Constructors`, JavaScript `// HTML helpers`.

- A label-style comment does not end with a period; a comment of two or more sentences punctuates every sentence, including the last. A single-sentence comment's terminal period is optional; either form is left as-is.
  - e.g., `// Constructors` (label); `// Playback has ended` and `// Playback has ended.` — both typical single-sentence forms.

- No documentation comments (Rust `///`/`//!`, Python docstrings, JSDoc `/** */`) anywhere except `python/pyxel/__init__.pyi`. The `.pyi` docstrings are regenerated by `scripts/generate_pyi_docstrings` and are not hand-edited.

- Every comment is understandable at its site without historical or external context. No self-referential gloss, no tautological phrasing.
  - e.g., `the Pyxel API (the API of Pyxel)` — anti-pattern (gloss restates the term); `// explanations to aid understanding` — anti-pattern (tautology).

#### Formatting

- Surface formatting (indentation, line wrapping, quoting) is delegated to `make format` for the file types it covers; hand-written `.md` is formatted by hand; every other file follows the standard conventions of its language or data format without unrelated reformatting.
  - e.g., a Rust match arm is not hand-aligned; a `Cargo.toml` table is not hand-reformatted.

- Exactly one blank line separates meaningful chunks unless `make format` prescribes otherwise. Runs of blank lines and blank lines inside a chunk are not used.
  - e.g., one blank line between class methods; no double blank between imports; no blank line between a function signature and its first statement.

#### Consistency

- Each file participates in every structurally comparable sibling group identified by a common directory, naming pattern, or shared role. Consistency is judged within each group; codebase-wide prevalence does not determine correctness.
  - e.g., sibling groups include `crates/pyxel-binding/src/*_wrapper.rs`; `python/pyxel/editor/widgets/*.py`; `python/pyxel/editor/*_editor.py`; HTML pages under `web/*/index.html`; language JSON files under `web/**/*.json`.

- A sibling group is an *exception group* only where this policy names the group, the convention it departs from, and the reason. The exception applies only to that convention; every other rule remains in force.
  - The exception groups are:
    - `crates/pyxel-binding/src/*_wrapper.rs`: mirrors the Python API rather than following Rust conventions (snake_case names, Python-style argument ordering, and Pyxel-historical short names like `blt`/`cls`/`pset` rather than the Rust-idiomatic counterparts in `pyxel-core`) and adopts the PyO3 binding conventions (`#[new]` for `__init__`, `#[getter]`/`#[setter]` for Python attributes);
    - SDL2 call sites: preserve the external SDL2 API's C-style names so calls remain recognizable against its documentation;
    - samples in `python/pyxel/examples/`: direct control flow and example-local names may stay when production-style decomposition or abstraction would make the sample harder to follow.

- Parallel mirrors — shapes deliberately repeated across sibling files for API symmetry or data-structure parallelism — preserve their shared structure. A correction is applied to every affected mirror rather than preserving a shared defect.
  - e.g., binding wrappers mirror the Python API one-to-one; image and tilemap drawing primitives mirror each other; each i18n JSON file repeats the `languages` array.

- Error and warning messages form codebase-wide families by failure kind rather than per-file groups: a message that mirrors a standard Python error keeps CPython's exact wording, a parameter constraint starts with the parameter as written, and any other message reuses its family's policy-compliant, language-idiomatic shape and casing rather than introducing a new form.
  - e.g., `fps must be greater than 0` and `scale must be greater than 0` — typical (one constraint family across files); a lone `draw: <message>` prefix among sentence-style siblings — anti-pattern.

- The `.pyi` API stub records each parameter's effective default — the value the implementation resolves to — while its binding may take `None` as a sentinel and resolve it internally. The `.pyi` default and the binding-signature default may therefore differ; that divergence is intentional, not an inconsistency.
  - e.g., the `.pyi` writes `init(title="Pyxel", fps=30, ...)` while the binding takes `Option` sentinels and resolves them; `None` stays in the `.pyi` only where `None` is itself the default behavior (`display_scale` auto, `colkey` / `font` none).

### Testing

Tests cover the product in four layers: Rust unit tests for platform-independent pure logic; Python API tests for the public interface surface; reference regression of screenshots from the bundled examples, apps, and editor plus rendered audio; and a manual pass on running samples for look, sound, and feel. Test code itself is in scope for every Source Code rule.

- A behavior is unit-tested when its breakage would not surface in the reference regression or the manual pass. These cases qualify:
  - numeric boundaries and degenerate inputs (zero, empty, maximum, negative);
  - rarely-taken branches (special syntax, edge inputs);
  - determinism contracts whose silent change alters existing users' assets;
  - compatibility surfaces (deprecated aliases keep working and warn);
  - save/load and serialization roundtrips;
  - error paths (exception type and message).
  - e.g., the BGM generator's seed-determinism snapshot — typical (a silent change rewrites existing users' music).

- A behavior already exercised by the reference regression and the manual pass, whose breakage is plainly visible or audible there, is not duplicated by an internal unit test.
  - e.g., a music mixing change is caught by the committed audio renders and the manual pass — typical; a unit test re-asserting the same waveform sample-by-sample — anti-pattern (duplicates the reference regression).

- A test verifies what its name and comments claim; a test that cannot fail for the claimed reason is fixed or removed.
  - e.g., a wraparound test whose inputs never wrap — anti-pattern.

- A deterministic outcome is pinned exactly. An assertion accepting several outcomes is reserved for genuine nondeterminism, with the source named in a comment.
  - e.g., `play_pos()` may be `None` right after `play()` (audio-thread timing) — typical; "level is 0.0 or 1.0" for a deterministic envelope — anti-pattern.

- Every automated test executes in `make test`.

### Documentation

#### Prose

- Documentation prose reads as natural technical writing in its own language, using the target language's standard conventions for compound-noun chains rather than literal translation from another language.
  - e.g., English "package installation guide" — typical; "installation of the package guide" — anti-pattern (translationese).

- Japanese text separates Japanese characters from adjacent alphanumeric tokens with a single half-width space, regardless of which file the text lives in; code spans keep their literal spacing.
  - e.g., 「Web 版 Pyxel」「16 色」「.pyxres ファイル」 — typical; 「Web版」「16色」 — anti-pattern (missing separation).

- Japanese technical loanwords follow the project's adopted spelling rather than a mechanical English-suffix rule. Unlisted terms follow established usage in comparable developer documentation, then stay consistent across sibling documentation.
  - Adopted spellings: 「ブラウザ」「エディタ」「パラメータ」「バッファ」「コンストラクタ」「ユーザー」「サーバー」「コンピュータ」.
  - e.g., 「ブラウザ上で実行」 — typical; mixing 「ブラウザ」 and 「ブラウザー」 for one concept in sibling pages — anti-pattern.

- Japanese text chooses parenthesis width by content: parentheses containing Japanese characters are full-width and sit flush; parentheses with ASCII-only content are half-width, separated by half-width spaces except when adjacent to punctuation.
  - e.g., 「イメージバンク（Image クラスのインスタンス）のリスト (0-2)」 — typical; 「リスト（0-2）」 — anti-pattern (full-width around ASCII-only content).

#### Translation

- The maintainer writes in Japanese; Japanese is the source of truth for translation. Translations route through English first, then to every other language.

- Each target language follows its own technical-writing conventions and retains established English loanwords where the target language conventionally uses them.
  - e.g., German, Spanish, Italian, and Portuguese keep loanwords like "Editor" and "Gamepad" in English; French instead uses native forms such as "éditeur" and "manette", keeping only product names like "Pyxel Editor" in English.

- A target-language translation is compared against the English version, not the Japanese source.
  - e.g., a German `"Installation des Pakets Anleitung"` mirrors a Japanese compound-noun chain and is rewritten as `"Paket-Installationsanleitung"`.

#### Proper Nouns

The authoritative Pyxel product names are: Pyxel, Pyxel Cube, Pyxel Editor, Pyxel Showcase, Pyxel Code Maker, Pyxel MML Studio, Pyxel Web Launcher, Pyxel User Examples, and Pyxel Composer. The abbreviations Pyxel Web (the web version), Pyxel MML (the MML variant), and Pyxel API (the public API) may stand in for their full forms.

- Listed product names are not translated and their casing is not altered.
  - e.g., `Pyxel Editor` in every language — never `pyxel editor`, `Pyxel-Editor`, or `ピクセルエディタ`.

- Every other proper noun retains the author's chosen representation, including hyphens, spacing, and casing.
  - e.g., `laser-jetman.html` keeps its hyphen; author-titled examples are not renamed to fit a `Pyxel`-prefixed pattern.

- A descriptive label may stand in for a product name when the surrounding context establishes the reference and the label reads naturally there. Outside such contexts, the product name follows the casing rule above.
  - e.g., a "Related Sites" section that introduces Pyxel Showcase as "the Pyxel community showcase" reads naturally; references to the same product elsewhere still write `Pyxel Showcase`.

### Release Notes

- A `CHANGELOG.md` entry exists when the change carries (a) a concrete user benefit, or (b) a debugger breadcrumb a future maintainer can follow. Changes that match neither are not recorded.
  - User benefits include: feature addition, bug fix, visible behavior change, performance improvement.
  - Breadcrumbs include: dependency update; shipped runtime update; build-toolchain update that affects release artifacts; build-configuration change that alters compilation; feature flag addition; internal runtime change; scoped refactor or cleanup; public API rename; release-process change.

- A breadcrumb names a concrete investigation surface; test-only, policy-only, and ignore-file changes are omitted unless they also change product, build, or release behavior.
  - e.g., `Updated pyo3 crate to version 0.29` remains useful, `Updated dependencies` is too broad, and adding tests or `.gitignore` entries is not a breadcrumb by itself.

- Sub-changes within a single commit are evaluated separately under the rule above.
  - e.g., a commit that fixes a bug and renames a public type produces two entries; a sub-change that is neither a user benefit nor a breadcrumb is omitted.

- Entries describe the change relative to the previous release. A change to code that has not shipped folds into the entry that introduces that code and does not produce its own entry.
  - e.g., a fix to a feature added earlier in the same unreleased version is absorbed by that feature's `Added` entry rather than gaining a `Fixed` entry.

- Each entry uses a language-idiomatic verb, grammar form, and level of object specificity. It matches compliant prior entries of the same change category for consistency; prevalence does not determine the form.

- Each entry fits a single line of at most 80 characters; entries typically run around 60 characters. An overlong entry is shortened without losing specificity and is split only when it contains independent sub-changes.
  - e.g., `Fixed Pyxel Editor color picker cursor shape across palette sizes` (65 chars) fits the typical band; independent user-visible changes become separate entries, while an atomic change is tightened into one line.

- Each entry is verified against the actual code diff, not the commit message. Commit messages may understate or misstate the diff.

- Documentation wording and translation touch-ups bundle into a single summary line.
  - e.g., `Update web titles and docs wording` covers a commit touching many doc strings.

## Verification

### Scope

- This policy applies to every file present in the audit target that is git-tracked or an intended addition and that `.gitattributes` does not mark as `binary`, including this file.

- Files excluded because they are toolchain output:
  - `*.tmx` (Tiled tilemap editor output)
  - `*.bdf` (font tooling output)
  - `Cargo.lock` and `*-lock.json` (package-manager lockfiles)
  - `web/styles.css` (a Tailwind CSS build artifact)
  - `.md` files whose first line begins with `<!-- This file is generated` (output of `scripts/generate_docs`)

- A file's code-side aspects (structure, syntax, identifiers, non-prose elements) remain in scope even when its prose content is reviewed separately.

### Format, Lint, and Test

- After a code change, `make format` runs before the commit.

- `make lint` (native build) and `make lint-wasm` (WebAssembly build) are warning-free at all times. The two builds use different feature sets and target environments; both pass.
  - Clippy warnings count as failures. Suppression with `#[allow(...)]` requires that the suppression itself be justified.

- After a code change, `make test` passes before completion is claimed. A flaky failure does not waive the rule; the failure is reproduced and the underlying cause fixed.

## Conventions of This File

- A new concern joins an existing section before a new section is added. A new section is warranted only when no existing section fits.
  - e.g., a wording guideline for CHANGELOG entries belongs under `Standards > Release Notes`, not as a top-level section.

- Individual past incidents are not recorded. An enduring lesson tightens the nearest existing rule; an example changes only when it clarifies a reusable boundary, replacing or rebalancing existing examples rather than accumulating another case.
  - e.g., a one-off false-positive finding belongs in a commit message or the contributor's working notes, not as a named bullet here.

- A section with an authoritative enumeration separates the list from the rules. The list appears either in the introductory prose, followed by rule bullets, or as sub-bullets or numbered items under the rule that needs the detail.
  - e.g., `Standards > Documentation > Proper Nouns` lists product names and abbreviations in its intro and uses bullets for casing rules; `Standards > Source Code > Performance` enumerates hot paths as sub-bullets under the rule that introduces them.

- Each rule may be followed by at most one compact `e.g.,` sub-bullet containing only the examples needed to clarify a reusable boundary.
  - Hypothetical anti-patterns read clearly as anti-patterns and are not asserted to exist in the code.
  - An `e.g.` line illustrates its rule and never substitutes for it; matching the example alone does not satisfy the rule.
  - A language-specific rule names the language in its rule statement.

- After revising any section, the whole file is re-read and its balance is confirmed. Adding or splitting a rule or authoritative enumeration triggers a review of structurally comparable peers for parallel gaps; a change confined to wording or examples does not. Proportionality is checked by section length and bullet count.

---

### Editor Manual (`docs/editor-manual.md`)

# Pyxel Editor Manual

*This document was auto-generated from the [Pyxel Editor Manual](https://kitao.github.io/pyxel/web/editor-manual/) web page, which also offers multilingual support.*

## Overview

Pyxel Editor is a built-in resource editing tool for Pyxel. It consists of four editors, which can be switched using the editor buttons at the top of the screen.

- **Image Editor:** Edit images (image banks) used for sprites and tiles
- **Tilemap Editor:** Arrange image bank images as tiles to compose maps
- **Sound Editor:** Edit sounds used for sound effects and melodies
- **Music Editor:** Arrange sounds in playback order to compose music such as BGM

## Getting Started

Launch Pyxel Editor with the following command:

pyxel edit [PYXEL_RESOURCE_FILE]

If the specified Pyxel resource file (.pyxres) exists, it will be loaded. If it does not exist, a new file with the specified name will be created. If the filename is omitted, `my_resource.pyxres` will be created.

## Common Operations

Operations common to all editors.

### Menu Bar

The menu bar at the top of the screen contains four editor switching buttons, Undo/Redo buttons, and a Save button. The help message area on the right displays context-sensitive information based on the cursor position.



-  Switch to Image Editor
-  Switch to Tilemap Editor
-  Switch to Sound Editor
-  Switch to Music Editor
-  Ctrl(Cmd)+Z — Undo
-  Ctrl(Cmd)+Y — Redo
-  Ctrl(Cmd)+S — Save

### Other Shortcuts

| Shortcut | Action |
| --- | --- |
| Alt(Option)+Left/Right | Switch between editor types |
| Shift+Ctrl(Cmd)+C | Copy the entire editing target |
| Shift+Ctrl(Cmd)+X | Cut the entire editing target (copy and clear) |
| Shift+Ctrl(Cmd)+V | Paste the copied target |
| Shift+Click ([+]/[-] buttons) | Change number or value in increments of ±10 |

### Using from Programs

The created resource file can be loaded and used in a Pyxel application with the `pyxel.load()` function.

### Switching Resource Files

While the editor is running, you can drag and drop another .pyxres file onto the window to load its contents. This operation overwrites all current edits, so be careful if you have unsaved changes.

## Image Editor

A mode for editing images in each image bank. You can create pixel art for sprites and tiles.

### Specifications

- **Image Banks:** 0–2 (3 banks)
- **Size:** 256×256 pixels each
- **Coordinate System:** Origin (0,0) at top-left, X+ rightward, Y+ downward

### Screen Layout



#### Image Canvas



The editing area selected in the image bank view is displayed magnified. Click and drag with the mouse to draw.

| Shortcut | Action |
| --- | --- |
| Right Click | Pick the color at the cursor position (color pick) |
| Right Drag | Pan the view |

#### Image Bank View



A thumbnail view of the entire image bank. The white frame indicates the current editing area.

| Shortcut | Action |
| --- | --- |
| Arrow Keys | Move the editing area (white frame) |
| Click | Move the editing area to the clicked position |
| Right Drag | Pan the view |

#### Tool Buttons



Select a tool for drawing.

-  S — Select a rectangular area
-  P — Draw freehand. Hold Shift for straight lines
-  R — Draw a rectangle outline. Hold Shift for a square
-  Shift+R — Draw a filled rectangle. Hold Shift for a square
-  C — Draw an ellipse outline. Hold Shift for a circle
-  Shift+C — Draw a filled ellipse. Hold Shift for a circle
-  B — Fill a connected region of the same color

#### Selection Tool Shortcuts

The following shortcut keys are available while using the selection tool.

| Shortcut | Action |
| --- | --- |
| Ctrl(Cmd)+A | Select the entire canvas |
| Ctrl(Cmd)+C | Copy the selection |
| Ctrl(Cmd)+X | Cut the selection (copy and clear) |
| Ctrl(Cmd)+V | Paste the copied area |
| H | Flip the selection horizontally |
| V | Flip the selection vertically |

#### Color Palette



Select a drawing color from the 16-color palette.

| Shortcut | Action |
| --- | --- |
| 1–8 | Select colors 0–7 |
| Shift+1–8 | Select colors 8–15 |

#### Bank Number



Switch the target image bank (0–2).

### Loading External Files

Drag and drop an image file such as PNG or JPG onto the Image Editor to load the image at the editing area position. The original image colors are automatically converted to Pyxel's color palette, and any parts exceeding the image bank boundaries are clipped.

## Tilemap Editor

A mode for arranging image bank images as tile patterns and editing tilemaps.

### Specifications

- **Tilemaps:** 0–7 (8 maps)
- **Size:** 256×256 tiles each
- **Tile Image:** 8×8 pixel region of the image bank
- **Reference Image Bank:** One per tilemap

### Screen Layout



#### Tilemap Canvas



The editing area selected in the tilemap view is displayed magnified. Place tile images selected in the tile image view. Drawing tools and selection shortcuts are shared with the Image Editor. Note that all cells default to tile (0,0), so it is recommended to keep image bank position (0,0) empty.

| Shortcut | Action |
| --- | --- |
| Right Click | Pick the tile at the cursor position (tile pick) |
| Right Drag | Pan the view |

#### Tilemap View



A thumbnail view of the entire tilemap. The white frame indicates the current editing area.

| Shortcut | Action |
| --- | --- |
| Arrow Keys | Move the editing area (white frame) |
| Click/Drag | Move the editing area to the clicked position |

#### Tile Image View



Displays the referenced image bank. Select tile images with the white frame.

| Shortcut | Action |
| --- | --- |
| Shift+Arrow Keys | Move the selection |
| Drag | Select multiple tiles in a rectangle |
| Right Drag | Pan the view |

#### Tilemap Number



Switch the target tilemap (0–7).

#### Ref. Image Bank Number



Switch the image bank (0–2) referenced for tile images.

#### Tool Buttons



Select a tool for drawing.

-  S — Select a rectangular area
-  P — Draw freehand. Hold Shift for straight lines
-  R — Draw a rectangle outline. Hold Shift for a square
-  Shift+R — Draw a filled rectangle. Hold Shift for a square
-  C — Draw an ellipse outline. Hold Shift for a circle
-  Shift+C — Draw a filled ellipse. Hold Shift for a circle
-  B — Fill a connected region of the same color

### Placing Multiple Tiles

When you draw with multiple tile images selected by dragging in the tile image view, the selected tiles are placed as a repeating pattern. For example, if you select two tiles vertically and draw with the pen, the two tiles are placed alternately in a repeating pattern. The same applies when selecting multiple tiles both horizontally and vertically. The drawing start position corresponds to the top-left of the selection.

### Loading TMX Files

Drag and drop a TMX file created with Tiled Map Editor ([Tiled](https://www.mapeditor.org/)) onto the Tilemap Editor to load the contents of layer 0 at the editing area position.

## Sound Editor

A mode for editing sounds used for melodies and sound effects.

### Specifications

- **Sounds:** 0–63 (64 sounds)
- **Pitch Range:** C0–B4
- **Max Length:** 48 notes
- **Polyphony:** 1 note (or rest)
- **Per-Note Settings:** Tone, volume, effect

### Screen Layout



#### Sound Number



Switch the target sound (0–63).

#### Playback Speed



Sets the playback duration per note. This is a common setting for all notes, specified in the range 1–99. Smaller values result in faster playback (1 speed unit = 1/120 second; e.g., speed 60 = 0.5 seconds per note). For sound effects, 1–9 is typical; for melodies, 10 or higher is recommended.

#### Play / Stop / Loop



-  Space — Play
-  Space — Stop (press Space during playback to stop)
-  L — Toggle loop playback

#### Piano Roll



Click to input note pitch. Red squares represent notes, blue squares represent rests. During piano roll editing, a blue cursor is displayed; during property area editing, a different cursor shape appears. Notes are played from left to right, and rests are automatically inserted in empty positions. Clicking an already-filled position overwrites it. Dragging inputs notes continuously with linear interpolation.

| Shortcut | Action |
| --- | --- |
| Up/Down | Switch between piano roll and property area |
| Left/Right | Move the cursor |
| Delete(fn+delete) | Delete the note at the current position |
| Backspace(delete) | Delete the note before the current position |
| Shift+Space | Play from the cursor position |
| Ctrl(Cmd)+A | Select the entire range |
| Shift+Left/Right | Start range selection |

#### Range Selection

| Shortcut | Action |
| --- | --- |
| Ctrl(Cmd)+C | Copy the selection |
| Ctrl(Cmd)+X | Cut the selection (copy and clear) |
| Ctrl(Cmd)+V | Paste the copied area |
| Ctrl(Cmd)+U | Raise the pitch of the selection by 1 |
| Ctrl(Cmd)+D | Lower the pitch of the selection by 1 |

#### Octave Bar



Displays the starting octave (0–3) for keyboard input. The keyboard covers two octaves from the selected octave.

| Shortcut | Action |
| --- | --- |
| PageUp(fn+Up) | Raise the octave by 1 |
| PageDown(fn+Down) | Lower the octave by 1 |

### Keyboard Input

You can input notes using the PC keyboard. The upper row (QWE row + number row) and the lower row (ZXC row + ASD row) each correspond to one octave of piano keys, covering two octaves simultaneously. White keys are assigned to natural notes and gray keys to sharps. Select the starting octave (0–3) with the octave bar.

**Upper Octave (QWE row + number row)**

| Note | Key |
| --- | --- |
| C | Q |
| C# | 2 |
| D | W |
| D# | 3 |
| E | E |
| F | R |
| F# | 5 |
| G | T |
| G# | 6 |
| A | Y |
| A# | 7 |
| B | U |

**Lower Octave (ZXC row + ASD row)**

| Note | Key |
| --- | --- |
| C | Z |
| C# | S |
| D | X |
| D# | D |
| E | C |
| F | V |
| F# | G |
| G | B |
| G# | H |
| A | N |
| A# | J |
| B | M |

| Rest | A |

| Shortcut | Action |
| --- | --- |
| Keyboard Key → Enter | Input the pressed keyboard note into the piano roll |
| 1 | Cycle the preview tone for keyboard input (4 types) |

#### Property Area (TON / VOL / EFX)



Set the tone (TON), volume (VOL), and effect (EFX) for each note. Press the ↓ key or click the TON row during piano roll editing to move to the property area. Cursor movement, range selection, copy & paste, and other operations are shared with the piano roll. Press a key on each row to input the corresponding value. For example, pressing P on the TON row sets Pulse. To change the tone partway through, set the value for all notes from the desired position onward. The same applies to VOL and EFX.

#### Tone

With the cursor on the TON row, press the following keys to set the tone. The default when not set is T (Triangle).

| Key | Name | Description |
| --- | --- | --- |
| `T` | Triangle | Triangle wave. Soft and gentle tone. Similar to a flute |
| `S` | Square | Square wave. Electronic and clear tone. Similar to a clarinet or organ |
| `P` | Pulse | Pulse wave. Bright and flashy tone. Similar to a trumpet |
| `N` | Noise | Noise. Unpitched sound. Suitable for percussion and explosion effects |

#### Volume

With the cursor on the VOL row, press number keys 0–7 to set the volume (0 = silent, 7 = maximum). The default when not set is 7. Using maximum volume on multiple channels simultaneously may result in excessive loudness.

#### Effect

With the cursor on the EFX row, press the following keys to set the effect. The default when not set is N (None).

| Key | Name | Description |
| --- | --- | --- |
| `N` | None | No effect |
| `S` | Slide | Smoothly glide the pitch from the previous note |
| `V` | Vibrato | Periodically oscillate the pitch |
| `F` | FadeOut | Gradually decrease the volume over the entire note. Also used to separate notes of the same pitch |
| `H` | Half-FadeOut | Gradually decrease the volume in the second half of the note. Longer sustain than F |
| `Q` | Quarter-FadeOut | Gradually decrease the volume in the last quarter of the note. Longer sustain than H |

## Music Editor

A mode for arranging sounds in playback order to compose music tracks.

### Specifications

- **Music Tracks:** 0–7 (8 tracks)
- **Channels:** 4
- **Sounds per Channel:** Max 32

### Screen Layout



#### Music Number



Switch the target music (0–7).

#### Play / Stop / Loop



-  Space — Play
-  Space — Stop (press Space during playback to stop)
-  L — Toggle loop playback

#### Sequence Editor (CH0–CH3)



Place sound numbers in playback order across four channels (CH0–CH3). Copy and paste between channels is also supported.

| Shortcut | Action |
| --- | --- |
| Up/Down | Switch the editing channel |
| Left/Right | Move the editing cursor |
| Delete(fn+delete) | Delete the sound at the current position |
| Backspace(delete) | Delete the sound before the current position |
| Shift+Space | Play from the cursor position |
| Ctrl(Cmd)+A | Select the entire range of the current channel |
| Shift+Arrow Keys | Select a range within the channel |
| Ctrl(Cmd)+C | Copy the selection |
| Ctrl(Cmd)+X | Cut the selection (copy and clear) |
| Ctrl(Cmd)+V | Paste the selection (also across channels) |
| Ctrl(Cmd)+U | Increment the sound number of the selection by 1 |
| Ctrl(Cmd)+D | Decrement the sound number of the selection by 1 |

#### Sound Buttons (0–63)



Buttons for inserting sound numbers (0–63). Pressing a button inserts the sound number at the editing cursor position. Created sounds are shown in blue, uncreated ones in gray. Hovering over a button previews the sound.

---

### Faq Ja (`docs/faq-ja.md`)

# Pyxel よくある質問

## Pyxel の学び方

<details>
<summary>Pyxel を学習するにはどこから始めればいいですか？</summary>

Pyxel のサンプルコードを以下の順に試すのがおすすめです。

1. 01_hello_pyxel — Pyxel の基本
2. 05_color_palette — カラーパレット
3. 03_draw_api — 描画 API
4. 04_sound_api — サウンド API
5. 02_jump_game — ゲーム実装

サンプルコードは `pyxel copy_examples` でコピーできるほか、[Pyxel Showcase](https://kitao.github.io/pyxel/web/showcase/) でブラウザ上でも実行できます。

</details>

<details>
<summary>Pyxel に関する書籍はありますか？</summary>

日本語版のみですが、[公式の書籍](https://gihyo.jp/book/2025/978-4-297-14657-3) が発売されています。

</details>

## API 仕様と使い方

<details>
<summary><code>update</code> 関数と <code>draw</code> 関数の違いは何ですか？</summary>

`update` 関数は毎フレーム呼び出されますが、`draw` 関数は処理時間が許容限界を超えた場合にスキップされることがあります。Pyxel はこの設計により、レンダリング負荷や OS の割り込み処理の影響を軽減して、滑らかなアニメーションを実現しています。

</details>

<details>
<summary>Pyxel MML の使い方を教えてください</summary>

MML (Music Macro Language) は、音符やテンポなどを文字列で記述してサウンドを定義する言語です。

Sound クラスの `mml` 関数に MML 文字列を渡すと、その Sound が MML の内容に沿って再生されるようになります。`mml()` を引数なしで呼ぶと MML 設定を解除できます。

```python
pyxel.sounds[0].mml("CDEFGAB>C")
```

`play` 関数にサウンド番号の代わりに MML 文字列を直接渡して再生することもできます。

```python
pyxel.play(0, "CDEFG")
```

利用できる MML コマンドは [Pyxel MML コマンド](https://kitao.github.io/pyxel/web/mml-studio/mml-commands.html) を参照してください。使用例はサンプル 09_shooter.py の [デモ](https://kitao.github.io/pyxel/web/showcase/examples/09-shooter.html) や [コード](https://github.com/kitao/pyxel/blob/main/python/pyxel/examples/09_shooter.py) で確認できます。

また、[Pyxel MML Studio](https://kitao.github.io/pyxel/web/mml-studio/) を使えば、MML をブラウザ上で作成・共有できます。

</details>

## ファイル操作とデータ管理

<details>
<summary>環境が変わるとファイルを読み込めなくなるのはなぜですか？</summary>

ファイルを読み込む際に、カレントディレクトリが意図したものになっているかを確認してください。

Pyxel の `init` 関数が呼ばれると、カレントディレクトリはそのスクリプトファイルと同じ場所に変更され、それ以降は相対パスでファイルを指定できます。しかし、`init` を呼ぶ前にファイルを開こうとした場合や `init` の後にカレントディレクトリを変更した場合には読み込みに失敗する可能性があります。

</details>

<details>
<summary>ハイスコアやゲームの進行状況など、アプリケーション固有のデータを保存する方法はありますか？</summary>

開発者名 (`vendor_name`) とアプリ名 (`app_name`) を `user_data_dir(vendor_name, app_name)` 関数に渡すと、プラットフォームに適したデータ保存用のディレクトリを作成し、そのパスを返します。このディレクトリの下でアプリケーション用のファイルの保存や読み込みを行ってください。

</details>

## Pyxel ツールの使い方

<details>
<summary>インストールせずに Pyxel を試せますか？</summary>

[Pyxel Code Maker](https://kitao.github.io/pyxel/web/code-maker/) を使えば、ブラウザ上で Pyxel アプリの作成・実行ができます。ただし、複数ファイル構成には対応していないため、本格的な開発にはローカル環境をおすすめします。

[Pyxel Showcase](https://kitao.github.io/pyxel/web/showcase/) では、サンプルコードやアプリをブラウザ上で一覧・実行できます。

</details>

<details>
<summary>自作の Pyxel アプリを Web で公開するにはどうすればいいですか？</summary>

Pyxel Web Launcher、app2html、HTML カスタム要素の 3 つの方法があります。詳しくは [Web 版 Pyxel の使い方](https://kitao.github.io/pyxel/web/web-usage/) を参照してください。

</details>

<details>
<summary>Pyxel Editor でパレットの色を変更できますか？</summary>

Pyxel リソースファイル (.pyxres) と同じディレクトリに、拡張子を .pyxpal に変えた同名のファイルを配置すると、Pyxel Editor のパレット表示色が変更されます。パレットファイルは `save_pal` 関数で作成できるほか、1 行に 1 色の RRGGBB 形式の 16 進数カラーコードを記述したテキストファイルとして手動で作成することもできます。

</details>

## バージョン移行ガイド

<details>
<summary>バージョン 2.4 への移行方法</summary>

Pyxel 2.4 ではサウンドエンジンと MML 文法が刷新されています。

コードをバージョン 2.4 に対応させるには、以下の変更を行ってください。

- Tone クラスの `waveform` フィールドを `wavetable` にリネームする
- `play` 関数、`playm` 関数の `tick` 引数を `sec`（小数形式の秒数）に変更する
- `play_pos` 関数の戻り値が `(sound_index, sec)` に変わったことに対応する
- Sound クラス、Music クラスの `save` 関数の `count` 引数を `sec` に変更する
- サウンドの再生秒数が必要な場合は、Sound クラスの `total_sec` 関数を利用する
- Sound クラスの `mml` 関数には新 MML 文法に沿ったコードを指定する（旧文法は自動検出される。`old_mml` 関数は非推奨）
- `save`、`load` 関数の `excl_*` オプションを `exclude_*` に変更する
- `save`、`load` 関数の `incl_*` オプションの指定を削除する

新しい MML 文法は上記の「[Pyxel MML の使い方](#api-仕様と使い方)」を参照してください。

</details>

## ライセンスとスポンサーシップ

<details>
<summary>Pyxel を作者の許可なく商業目的で使用することはできますか？</summary>

MIT ライセンスに従い、ソースコードやライセンス表示用のファイルに著作権およびライセンスの全文を明示すれば、作者の許可を得ることなく自由に販売や配布が可能です。ただし、Pyxel は個人で開発しているため、作者へのご連絡やスポンサーによるご支援をいただけるとありがたいです。

</details>

---

### Faq (`docs/faq.md`)

# Pyxel FAQ

## Learning Pyxel

<details>
<summary>Where do I start to learn Pyxel?</summary>

We recommend trying Pyxel's example code in the following order.

1. 01_hello_pyxel — Pyxel basics
2. 05_color_palette — Color palette
3. 03_draw_api — Drawing API
4. 04_sound_api — Sound API
5. 02_jump_game — Game implementation

You can copy the examples with `pyxel copy_examples`, or run them in your browser on [Pyxel Showcase](https://kitao.github.io/pyxel/web/showcase/).

</details>

<details>
<summary>Are there any books on Pyxel?</summary>

The official [book](https://gihyo.jp/book/2025/978-4-297-14657-3) is available in Japanese only.

</details>

## API Specification and Usage

<details>
<summary>What is the difference between the <code>update</code> and <code>draw</code> functions?</summary>

The `update` function is called every frame, but the `draw` function may be skipped if the processing time exceeds the allowable limit. Pyxel uses this design to reduce the impact of rendering load and OS interrupts, enabling smooth animation.

</details>

<details>
<summary>How do I use Pyxel MML?</summary>

MML (Music Macro Language) is a language for defining sounds by describing notes, tempo, and other parameters as a string.

Pass an MML string to the Sound class's `mml` function, and that Sound will play back following the MML. Calling `mml()` with no arguments clears the MML setting.

```python
pyxel.sounds[0].mml("CDEFGAB>C")
```

You can also play an MML string directly by passing it to the `play` function instead of a sound number.

```python
pyxel.play(0, "CDEFG")
```

For available MML commands, see [Pyxel MML Commands](https://kitao.github.io/pyxel/web/mml-studio/mml-commands.html). For usage examples, see the [demo](https://kitao.github.io/pyxel/web/showcase/examples/09-shooter.html) and [code](https://github.com/kitao/pyxel/blob/main/python/pyxel/examples/09_shooter.py) of the 09_shooter.py example.

You can also create and share MML in your browser using [Pyxel MML Studio](https://kitao.github.io/pyxel/web/mml-studio/).

</details>

## File Operations and Data Management

<details>
<summary>Why does file loading fail when the environment changes?</summary>

Make sure that the current directory is set as intended when loading files.

When Pyxel's `init` function is called, the current directory is changed to the same location as the script file. After that, files can be specified using relative paths. However, loading may fail if you try to open a file before calling `init` or if the current directory is changed after calling `init`.

</details>

<details>
<summary>How can I save application-specific data like high scores or game progress?</summary>

Pass the developer name (`vendor_name`) and application name (`app_name`) to the `user_data_dir(vendor_name, app_name)` function. It will create a directory suitable for data storage on the current platform and return its path. Use this directory to save and load your application's files.

</details>

## Using Pyxel Tools

<details>
<summary>Can I try Pyxel without installing it?</summary>

With [Pyxel Code Maker](https://kitao.github.io/pyxel/web/code-maker/), you can create and run Pyxel apps in your browser. However, it does not support multi-file projects, so a local environment is recommended for full-scale development.

[Pyxel Showcase](https://kitao.github.io/pyxel/web/showcase/) lets you browse and run sample code and apps in your browser.

</details>

<details>
<summary>How do I publish my Pyxel app on the web?</summary>

There are three methods: Pyxel Web Launcher, app2html, and HTML Custom Elements. For details, see [How to Use Pyxel for Web](https://kitao.github.io/pyxel/web/web-usage/).

</details>

<details>
<summary>Can I change the palette colors in Pyxel Editor?</summary>

If you place a file with the same name but the .pyxpal extension in the same directory as the Pyxel resource file (.pyxres), Pyxel Editor will display the palette using those colors. Palette files can be created with the `save_pal` function, or manually as a text file with one RRGGBB hex color per line.

</details>

## Migration Guide

<details>
<summary>How to migrate code to version 2.4</summary>

In Pyxel 2.4, the sound engine and MML syntax have been revamped.

To make your code compatible with version 2.4, please make the following changes:

- Rename the `waveform` field of the Tone class to `wavetable`
- Change the `tick` argument of the `play` and `playm` functions to `sec` (a float value in seconds)
- Update code to handle the return value of the `play_pos` function, which is now `(sound_index, sec)`
- Change the `count` argument of the `save` function in the Sound and Music classes to `sec`
- If you need the playback duration of a sound, use the `total_sec` function of the Sound class
- For the Sound class's `mml` function, use code that follows the new MML syntax (old syntax is auto-detected; the `old_mml` function is deprecated)
- Change the `excl_*` option in the `save` and `load` functions to `exclude_*`
- Remove the `incl_*` option from the `save` and `load` functions

For details on the new MML syntax, see "[How do I use Pyxel MML?](#api-specification-and-usage)" above.

</details>

## Licensing and Sponsorship

<details>
<summary>Can I use Pyxel for commercial purposes without the author's permission?</summary>

As long as you comply with the MIT License and clearly display the full text of the copyright and license in the source code or license file, you are free to sell or distribute your work without the author's permission. That said, Pyxel is developed by a single individual, so reaching out to the author or becoming a sponsor would be greatly appreciated.

</details>

---

### Mml Commands (`docs/mml-commands.md`)

# Pyxel MML Commands

*This document was auto-generated from the [Pyxel MML Commands](https://kitao.github.io/pyxel/web/mml-studio/) web page, which also offers multilingual support.*

| Command | Description |
| --- | --- |
| `T <bpm>` | Sets the tempo (BPM). Range: 1-. Default is 120. |
| `Q <gate_percent>` | Sets the gate time as a percentage. Range: 0-100. 100 means the note is played with no gap, 0 means it is not played at all. Default is 80. |
| `@ <tone>` | Sets the tone. Range: 0-3 (by default: 0:Triangle / 1:Square / 2:Pulse / 3:Noise). Default is 0. |
| `V <vol>` | Sets the volume. Range: 0-127. Default is 100. |
| `K <key_offset>` | Sets the transpose amount in semitones. 12 raises the pitch by one octave. Default is 0. |
| `Y <offset_cents>` | Sets detune in cents. 100 raises by a semitone, -100 lowers by a semitone. Default is 0. |
| `@ENV <slot>` | Switches the envelope (volume curve) slot. Range: 0-. 0 turns it off. |
| `@ENV <slot> { init_vol, dur_ticks1, vol1, dur_ticks2, vol2, ... }` | Slot range: 1-. Sets and switches to the specified envelope slot. Slot 0 cannot be specified. Inside `{ }`, specify "initial volume (once)", then repeat "duration (tick), volume (vol)". 1 tick is 1/48 of a quarter note. Example: `@ENV1 { 30, 20, 100, 50, 0 }` |
| `@VIB <slot>` | Switches the vibrato (pitch modulation) slot. Range: 0-. 0 turns it off. |
| `@VIB <slot> { delay_ticks, period_ticks, depth_cents }` | Slot range: 1-. Sets and switches to the specified vibrato slot. Slot 0 cannot be specified. Inside `{ }`, specify "delay (tick), period (tick), depth (cent)". 1 tick is 1/48 of a quarter note. Example: `@VIB1 { 24, 12, 100 }` |
| `@GLI <slot>` | Switches the glide (pitch slide) slot. Range: 0-. 0 turns it off. |
| `@GLI <slot> { offset_cents, dur_ticks }` | Slot range: 1-. Sets and switches to the specified glide (pitch slide) slot. Slot 0 cannot be specified. Inside `{ }`, specify "initial pitch offset (cent), time to return to 0 (tick)". 1 tick is 1/48 of a quarter note. Specifying `*` for each parameter automatically applies the pitch offset as the difference from the previous note and the return time as the playback duration of each note, respectively. Example: `@GLI1 { -100, 24 }` |
| `O <oct>` | Sets the octave. Range: -1-9. `O4`'s A is 440 Hz. Default is 4. |
| `>` | Raises the octave by 1 (max 9). |
| `<` | Lowers the octave by 1 (min -1). |
| `L <len>` | Sets the default note/rest length. Range: 1-192. `L4` is a quarter note, `L8` is an eighth note, and `L12` is an eighth note triplet. Default is 4. |
| `C/D/E/F/G/A/B` | Plays the specified note. You can specify the length after the note, e.g., `F16`. |
| `R` | Rest. Range: 1-192. You can specify the length after the rest, e.g., `R8`. |
| `#` | Raises the note by a semitone. |
| `+` | Raises the note by a semitone. |
| `-` | Lowers the note by a semitone. |
| `.` | Dotted note/rest. Extends the length by half. Can be repeated for multiple dots. |
| `&` | Tie/Slur. When connecting the same pitch, it ties the notes into one. When connecting different pitches, it plays them legato (no gap). You can also specify only the length, e.g., `C4&16`. |
| `[` | Start of repeat section. |
| `] <count>` | End of repeat section. Count range: 1-. Repeats the section between `[` and `]` the specified number of times. If omitted, repeats infinitely. Nested repeats are supported. |

---

### Pyxres Format (`docs/pyxres-format.md`)

# Pyxel Resource File Format (.pyxres)

This document describes the internal format of Pyxel resource files (`.pyxres`).

## Overview

A `.pyxres` file is a **ZIP archive** containing a single TOML file named `pyxel_resource.toml`. All resource data — images, tilemaps, sounds, and music — is stored as human-readable text in TOML format.

An optional **palette file** (`.pyxpal`) can accompany the resource file to define a custom color palette.

## File Structure

```text
example.pyxres (ZIP archive)
└── pyxel_resource.toml
```

```text
example.pyxpal (optional, same directory)
```

## TOML Format

The `pyxel_resource.toml` file has the following top-level structure:

```toml
format_version = 1

[[images]]
...

[[tilemaps]]
...

[[sounds]]
...

[[musics]]
...
```

The `format_version` field and all four section arrays (`images`, `tilemaps`, `sounds`, `musics`) are **required**. Empty fields inside an entry use empty arrays (e.g., `notes = []`), and a section intentionally skipped by `save(..., exclude_*)` is written as an empty array. A normal save writes the runtime banks (3 images, 8 tilemaps, 64 sounds, 8 musics); files with fewer entries are accepted on load.

Pyxel currently writes `format_version = 1` for maximum backward compatibility. On load, files with format version up to **4** (the current maximum) are accepted. Files that contain the legacy archive layout (`pyxel_resource/version` + separate files) are detected and loaded with automatic conversion.

## Images

Up to **3 image banks**, each **256×256 pixels**. Each pixel is a palette color index (`u8`, 0–15 for the default palette; 0–255 for the extended palette of up to 256 colors).

```toml
[[images]]
width = 256
height = 256
data = [[0, 0, 10, 12, 12, 0, ...], [0, 7, 7, ...], ...]
```

All fields are required.

| Field | Type | Description |
| --- | --- | --- |
| `width` | u32 | Image width in pixels |
| `height` | u32 | Image height in pixels |
| `data` | array of arrays of u8 | 2D pixel data, one sub-array per row. Each value is a color index |

**Compression:** Trailing elements in each row that repeat the last distinct value are omitted. Trailing rows that are identical to the last distinct row are also omitted. When loading, omitted values are filled by repeating the last value.

For example, a row `[0, 0, 5, 5, 5, 5]` is stored as `[0, 0, 5]`, and an image where the last 200 rows are all `[0]` stores only the first 56 rows plus one `[0]` row.

## Tilemaps

Up to **8 tilemaps**, each **256×256 tiles**. Each tile references an 8×8 pixel region in an image bank.

```toml
[[tilemaps]]
width = 256
height = 256
imgsrc = 0
data = [[10, 6, 11, 6, 12, 6, 0, 0, ...], ...]
```

All fields are required.

| Field | Type | Description |
| --- | --- | --- |
| `width` | u32 | Tilemap width in tiles |
| `height` | u32 | Tilemap height in tiles |
| `imgsrc` | u32 | Source image bank index (0–2) |
| `data` | array of arrays of u16 | 2D tile data in interleaved format (see below) |

Each tile in the tilemap is a coordinate pair `(tile_x, tile_y)` (`u16`, `u16`) pointing to the position of the 8×8 tile in the source image bank. In the TOML data, these pairs are **interleaved** within each row: `[tx0, ty0, tx1, ty1, ...]`, so each row has `width × 2` elements.

For example, a tilemap row of 3 tiles referencing image positions (10, 6), (11, 6), and (12, 6) is stored as `[10, 6, 11, 6, 12, 6]`.

The same trailing-value compression as images is applied.

## Sounds

Up to **64 sounds**. Each sound is a sequence of notes with per-note tone, volume, and effect settings.

```toml
[[sounds]]
notes = [28, 28, 24, 19, -1, -1]
tones = [2]
volumes = [6]
effects = [2, 3, 3, 0]
speed = 25
```

All fields are required. Empty sounds use empty arrays (e.g., `notes = []`).

| Field | Type | Description |
| --- | --- | --- |
| `notes` | array of i8 | Note values. -1 = rest, 0 (C0) to 59 (B4) |
| `tones` | array of u8 | 0 = Triangle, 1 = Square, 2 = Pulse, 3 = Noise |
| `volumes` | array of u8 | 0 (silent) to 7 (max) |
| `effects` | array of u8 | 0 = None, 1 = Slide, 2 = Vibrato, 3 = FadeOut, 4 = Half-FadeOut, 5 = Quarter-FadeOut |
| `speed` | u16 | Playback speed in ticks per note (1 tick = 1/120 second). Default: 30 |

The `tones`, `volumes`, and `effects` arrays are stored exactly as set on the sound and may be shorter than `notes`. During playback, values are read cyclically (index modulo array length), and an empty array falls back to the default: tone 0 (Triangle), volume 7, effect 0 (None).

**Note mapping:** Notes are encoded as `base + octave × 12`, where C=0, D=2, E=4, F=5, G=7, A=9, B=11, and octave ranges from 0 to 4. For example, C0=0, A4=57, B4=59.

**MML-defined sounds:** Sounds defined with `Sound.mml()` cannot be saved to `.pyxres`; only the `notes`, `tones`, `volumes`, `effects`, and `speed` fields are persisted. To preserve MML-based sounds, keep the MML strings in your source code and call `sound.mml()` at runtime.

## Musics

Up to **8 music tracks**. Each track arranges sounds across up to **4 channels** for sequential playback.

```toml
[[musics]]
seqs = [[0, 1], [2, 3], [4]]
```

The `seqs` field is required. Empty music uses an empty array (`seqs = []`).

| Field | Type | Description |
| --- | --- | --- |
| `seqs` | array of arrays of u32 | Sound sequences per channel. `seqs[0]` is channel 0, `seqs[1]` is channel 1, etc. Each value is a sound index (0–63) |

Trailing empty channels are omitted when saving.

## Palette File (.pyxpal)

An optional text file with one RGB color per line in 6-digit hexadecimal format. The file uses the same base name as the `.pyxres` file (e.g., `sample.pyxres` → `sample.pyxpal`).

```text
000000
2b335f
7e2072
19959c
...
```

| Property | Value |
| --- | --- |
| Default colors | 16 |
| Maximum colors | 256 |
| Format | One `RRGGBB` hex value per line |

If no `.pyxpal` file is present, the built-in default palette is used:

| Index | Color | Hex |
| --- | --- | --- |
| 0 | Black | `000000` |
| 1 | Navy | `2b335f` |
| 2 | Purple | `7e2072` |
| 3 | Green | `19959c` |
| 4 | Brown | `8b4852` |
| 5 | Dark Blue | `395c98` |
| 6 | Light Blue | `a9c1ff` |
| 7 | White | `eeeeee` |
| 8 | Red | `d4186c` |
| 9 | Orange | `d38441` |
| 10 | Yellow | `e9c35b` |
| 11 | Lime | `70c6a9` |
| 12 | Cyan | `7696de` |
| 13 | Gray | `a3a3a3` |
| 14 | Pink | `ff9798` |
| 15 | Peach | `edc7b0` |

---

### User Guide (`docs/user-guide.md`)

# Pyxel User Guide

*This document was auto-generated from the [Pyxel User Guide](https://kitao.github.io/pyxel/web/user-guide/) web page, which also offers multilingual support.*



**Pyxel** (/ˈpɪksəl/) is a retro game engine for Python. With simple specifications inspired by retro gaming consoles, such as displaying only 16 colors and supporting 4 sound channels, you can easily enjoy making pixel-art-style games.

Pyxel is open source under the [MIT License](https://github.com/kitao/pyxel/blob/main/LICENSE) and free to use. Let's start making retro games with Pyxel!

Also, this project is developed by one person, kept going by your [GitHub](https://github.com/kitao/pyxel) stars. If you enjoy it, please give it a star!

## Specifications

- Runs on Windows, Mac, Linux, and Web
- Programming in Python
- Customizable screen size
- 16-color palette
- 3 256x256 image banks
- 8 256x256 tilemaps
- 4 channels with 64 definable sounds
- 8 music tracks that can combine any sounds
- Keyboard, mouse, and gamepad inputs
- Image and sound editing tools
- User-extensible colors, sound channels, and banks

### Color Palette





[GIMP palette file (.gpl)](pyxel.gpl)

## How to Install

### Windows / Mac / Linux

After installing [Python 3](https://www.python.org/) (version 3.11 or higher), run the following command:

```sh
pip install -U pyxel
```

**Note:** On Windows, if installing Python using the installer from python.org, make sure to check `Add python.exe to PATH` to enable the `pyxel` command.

### Web

The web version of Pyxel can be used without installation on PCs, smartphones, tablets, and other devices with a compatible browser.

The web-based development environment [Pyxel Code Maker](https://kitao.github.io/pyxel/web/code-maker/) is ready to use simply by opening it in your browser.

For other usage, such as embedding Pyxel apps on your own site, please refer to [How to Use Pyxel for Web](https://kitao.github.io/pyxel/web/web-usage/).

### VS Code

By adding the Pyxel extension to [Visual Studio Code](https://code.visualstudio.com/) (VS Code), you can develop and run Pyxel apps without installing Python or Pyxel.

To add the Pyxel extension, search for "[Pyxel](https://marketplace.visualstudio.com/items?itemName=kitao.pyxel-vscode)" in the VS Code Extensions view and click the install button.

## Basic Usage

### Pyxel Command

Installing Pyxel adds the `pyxel` command. Specify a command name after `pyxel` to perform various operations.

Run it without arguments to see the list of available commands:

```sh
pyxel
```
```
Pyxel, a retro game engine for Python
usage:
    pyxel run PYTHON_SCRIPT_FILE(.py)
    pyxel watch WATCH_DIR PYTHON_SCRIPT_FILE(.py)
    pyxel play PYXEL_APP_FILE(.pyxapp)
    pyxel edit [PYXEL_RESOURCE_FILE(.pyxres)]
    pyxel package APP_DIR STARTUP_SCRIPT_FILE(.py)
    pyxel app2exe PYXEL_APP_FILE(.pyxapp)
    pyxel app2html PYXEL_APP_FILE(.pyxapp)
    pyxel copy_examples
```

### Try Examples

The following command copies Pyxel examples to the current directory:

```sh
pyxel copy_examples
```

You can run examples locally with the following commands:

```sh
# Run example in examples directory
cd pyxel_examples
pyxel run 01_hello_pyxel.py

# Run app in examples/apps directory
cd apps
pyxel play 30sec_of_daylight.pyxapp
```

Examples can also be viewed and run in the browser from [Pyxel Showcase](https://kitao.github.io/pyxel/web/showcase/).

## Creating Applications

### Create a Program

In your Python script, import Pyxel, set the window size with `init`, and start the application with `run`.

```python
import pyxel

pyxel.init(160, 120)


def update():
    if pyxel.btnp(pyxel.KEY_Q):
        pyxel.quit()


def draw():
    pyxel.cls(0)
    pyxel.rect(10, 10, 20, 20, 11)


pyxel.run(update, draw)
```

The arguments of the `run` function are the `update` function, which processes frame updates, and the `draw` function, which handles screen drawing.

In an actual application, it is recommended to wrap Pyxel code in a class, as shown below:

```python
import pyxel


class App:
    def __init__(self):
        pyxel.init(160, 120)
        self.x = 0
        pyxel.run(self.update, self.draw)

    def update(self):
        self.x = (self.x + 1) % pyxel.width

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(self.x, 0, 8, 8, 9)


App()
```

For creating simple graphics without animation, you can use the `show` function to simplify your code.

```python
import pyxel

pyxel.init(120, 120)
pyxel.cls(1)
pyxel.circb(60, 60, 40, 7)
pyxel.show()
```

### Run a Program

A created script can be executed using the `python` command:

```sh
python PYTHON_SCRIPT_FILE
```

It can also be run with the `pyxel run` command:

```sh
pyxel run PYTHON_SCRIPT_FILE
```

Additionally, the `pyxel watch` command monitors changes in a specified directory and automatically re-runs the program when changes are detected:

```sh
pyxel watch WATCH_DIR PYTHON_SCRIPT_FILE
```

Stop directory monitoring by pressing `Ctrl(Cmd)+C`.

### Special Key Controls

The following special key actions are available while a Pyxel application is running:

| Shortcut | Action |
| --- | --- |
| Esc | Quit the application |
| Alt(Option)+R or A+B+X+Y+BACK on gamepad | Reset the application |
| Alt(Option)+1 | Save the screenshot to the desktop |
| Alt(Option)+2 | Reset the recording start time of the screen capture video |
| Alt(Option)+3 | Save a screen capture video to the desktop (up to 10 seconds) |
| Alt(Option)+8 or A+B+X+Y+DL on gamepad | Toggle screen scaling between maximum and integer |
| Alt(Option)+9 or A+B+X+Y+DR on gamepad | Switch between screen modes (Crisp/Smooth/Retro) |
| Alt(Option)+0 or A+B+X+Y+DU on gamepad | Toggle the performance monitor (FPS/`update` time/`draw` time) |
| Alt(Option)+Enter or A+B+X+Y+DD on gamepad | Toggle fullscreen |
| Shift+Alt(Option)+1/2/3 | Save image bank 0, 1, or 2 to the desktop |
| Shift+Alt(Option)+0 | Save the current color palette to the desktop |

## Creating Resources

### Pyxel Editor

Pyxel Editor creates images and sounds used in a Pyxel application.
The created data is saved as a Pyxel resource file (.pyxres).

For detailed usage instructions, see the [Pyxel Editor Manual](https://kitao.github.io/pyxel/web/editor-manual/).

### Other Creation Methods

Pyxel images and tilemaps can also be created using the following methods:

- Create images or tilemaps from lists of strings with the `Image.set` or `Tilemap.set` functions
- Load palette-ready image files (PNG/GIF/JPEG) with the `Image.load` function

Pyxel sounds and music can also be created using the following method:

- Create them from strings with the `Sound.set` or `Music.set` functions

Refer to the API reference for the use of these functions.

## Distributing Applications

Pyxel supports a cross-platform distribution format called a Pyxel application file.

Create a Pyxel application file (.pyxapp) with the `pyxel package` command:

```sh
pyxel package APP_DIR STARTUP_SCRIPT_FILE
```

If you need to include resources or additional modules, place them in the application directory.

Metadata can be displayed at runtime by specifying it in the following format within the startup script. Fields other than `title` and `author` are optional.

```python
# title: Pyxel Platformer
# author: Takashi Kitao
# desc: A Pyxel platformer example
# site: https://github.com/kitao/pyxel
# license: MIT
# version: 1.0
```

The created application file can be run using the `pyxel play` command:

```sh
pyxel play PYXEL_APP_FILE
```

A Pyxel application file can also be converted to an executable or an HTML file using the `pyxel app2exe` or `pyxel app2html` commands.

## API Reference

A complete list of Pyxel APIs is available at [Pyxel API Reference](https://kitao.github.io/pyxel/web/api-reference/).

Pyxel also includes advanced APIs that require specialized knowledge. You can view them by checking the "Advanced" checkbox on the reference page.

If you're confident in your skills, try using the Advanced API to create truly amazing works!

## Links

### Documentation

- [Pyxel API Reference](https://kitao.github.io/pyxel/web/api-reference/)
- [Pyxel MML Commands](https://kitao.github.io/pyxel/web/mml-studio/mml-commands.html)
- [Pyxel Editor Manual](https://kitao.github.io/pyxel/web/editor-manual/)
- [How to Use Pyxel for Web](https://kitao.github.io/pyxel/web/web-usage/)
- [Pyxel Resource File Format](https://github.com/kitao/pyxel/blob/main/docs/pyxres-format.md)
- [FAQ](https://github.com/kitao/pyxel/blob/main/docs/faq.md)

### Examples & Tools

- [Pyxel Showcase](https://kitao.github.io/pyxel/web/showcase/)
- [Pyxel Web Launcher](https://kitao.github.io/pyxel/web/launcher/)
- [Pyxel Code Maker](https://kitao.github.io/pyxel/web/code-maker/)
- [Pyxel MML Studio](https://kitao.github.io/pyxel/web/mml-studio/)
- [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=kitao.pyxel-vscode)
- [MCP Server](https://github.com/kitao/pyxel-mcp)
- [Claude Code Skill](https://github.com/kitao/pyxel-skill)

### Other Information

- [User Examples](https://kitao.github.io/pyxel-user-examples/)
- [Developer's X Account](https://x.com/kitao)
- [Discord Server (English)](https://discord.gg/Z87eYHN)
- [Discord Server (Japanese)](https://discord.gg/qHA5BCS)
- [Official Guidebook (Japanese)](https://gihyo.jp/book/2025/978-4-297-14657-3)

## How to Contribute

### Submitting Issues

Use the [Issue Tracker](https://github.com/kitao/pyxel/issues) to submit bug reports and feature or enhancement requests. Before submitting a new issue, make sure there are no similar open issues.

### Functional Testing

Anyone who manually tests the code and reports bugs or suggestions for enhancements in the [Issue Tracker](https://github.com/kitao/pyxel/issues) is very welcome!

### Submitting Pull Requests

Patches and fixes are accepted in the form of pull requests (PRs). Make sure that the issue the pull request addresses is open in the Issue Tracker.

Submitting a pull request implies that you agree to license your contribution under the [MIT License](https://github.com/kitao/pyxel/blob/main/LICENSE).

## License

Pyxel is licensed under the [MIT License](https://github.com/kitao/pyxel/blob/main/LICENSE). It can be reused in proprietary software, provided that all copies of the software or its substantial portions include a copy of the MIT License terms and a copyright notice.

## Recruiting Sponsors

Pyxel is looking for sponsors on GitHub Sponsors. Please consider sponsoring Pyxel to support its continued maintenance and feature development. As a benefit, sponsors can consult directly with the Pyxel developer. For more details, please visit [here](https://github.com/sponsors/kitao).

---

### Web Usage (`docs/web-usage.md`)

# How to Use Pyxel for Web

*This document was auto-generated from the [How to Use Pyxel for Web](https://kitao.github.io/pyxel/web/web-usage/) web page, which also offers multilingual support.*

Pyxel for Web is a version of Pyxel that runs in web browsers using WebAssembly technology. No installation of Python or Pyxel is required, and it is accessible from PCs, smartphones, and tablets.

## Ways to Use Pyxel for Web

There are four ways to use Pyxel for Web.

| Method | Description |
| --- | --- |
| Pyxel Code Maker | An online development environment for writing and running code in the browser |
| Pyxel Web Launcher | Run a GitHub repository in the browser by specifying its URL |
| app2html Command | Convert a Pyxel application file (.pyxapp) to an HTML file |
| HTML Custom Elements | Add HTML custom elements to run Pyxel apps on any HTML page |

## Pyxel Code Maker

[Pyxel Code Maker](https://kitao.github.io/pyxel/web/code-maker/) is an online development environment that lets you develop and run Pyxel apps in the browser without installing Python or Pyxel. Just open it in your browser with no setup required, making it a great way to try Pyxel.

For detailed usage, see the [Pyxel Code Maker Manual](https://kitao.github.io/pyxel/web/code-maker/manual.html).

## Pyxel Web Launcher

[Pyxel Web Launcher](https://kitao.github.io/pyxel/web/launcher/) is a tool that runs Python scripts and Pyxel apps (.pyxapp) published on GitHub simply by specifying their URL.

Launch URLs can be created on the [Pyxel Web Launcher](https://kitao.github.io/pyxel/web/launcher/) page by entering the required information, or manually using the format below. Note that Pyxel Web Launcher always uses the latest version of Pyxel.

### URL Format

```
https://kitao.github.io/pyxel/web/launcher/?<command>=<username>/<repository>/<branch>/<path>/<filename without extension>
```

### Commands

| Command | Action |
| --- | --- |
| `run` | Run a Python script (.py) |
| `play` | Run a Pyxel app (.pyxapp) |
| `edit` | Edit a resource file (.pyxres) in Pyxel Editor |

### URL Examples

To run `src/scenes/title.py` in user `taro`'s repository `my_repo`, branch `main`:

```
https://kitao.github.io/pyxel/web/launcher/?run=taro/my_repo/main/src/scenes/title
```

To run `dist/games/shooter.pyxapp` in the same repository:

```
https://kitao.github.io/pyxel/web/launcher/?play=taro/my_repo/main/dist/games/shooter
```

### Additional Parameters

The following additional parameters can be specified for the `run` and `play` commands.

| Parameter | Description |
| --- | --- |
| `gamepad=enabled` | Display a virtual gamepad on touch devices (smartphones and tablets) |

Example URL with additional parameters for the `run` command:

```
https://kitao.github.io/pyxel/web/launcher/?run=taro/my_repo/main/src/scenes/title&gamepad=enabled
```

The `edit` command has the `editor` parameter to specify which editor to launch: `image`, `tilemap`, `sound`, `music`

Example URL for the `edit` command:

```
https://kitao.github.io/pyxel/web/launcher/?edit=taro/my_repo/main/assets/shooter&editor=tilemap
```

Apps consisting of multiple Python files load faster when bundled into a `.pyxapp` file with `pyxel package` and run with `play`.

## app2html Command

The `pyxel app2html` command converts a Pyxel application file (.pyxapp) into a standalone HTML file.

```sh
pyxel app2html your_app.pyxapp
```

All code and resource data are embedded in the HTML, so the generated file can be published simply by distributing it.

The Pyxel version is pinned to the one used at conversion time, so future updates will not affect behavior.

The virtual gamepad is enabled by default and automatically displayed on touch devices. To disable it, remove `gamepad: "enabled"` from the generated HTML.

## HTML Custom Elements

By adding HTML custom elements provided by Pyxel to an HTML file, you can run Pyxel apps on any HTML page.

### Setup

Add the following script tag to your HTML. If no version is specified, the latest version of Pyxel is always used.

```html
<script src="https://cdn.jsdelivr.net/gh/kitao/pyxel/wasm/pyxel.js"></script>
```

You can pin the Pyxel version by specifying a version number after `@`.

```html
<script src="https://cdn.jsdelivr.net/gh/kitao/pyxel@v2.8.7/wasm/pyxel.js"></script>
```

Loading this script makes three HTML custom elements available, corresponding to Pyxel's `run`, `play`, and `edit` commands: `pyxel-run`, `pyxel-play`, and `pyxel-edit`.

### pyxel-run

A tag for running Python scripts.

To run Python code directly, write the code in the `script` attribute.

```html
<pyxel-run
  script="
import pyxel
pyxel.init(200, 150)
pyxel.cls(8)
pyxel.line(20, 20, 180, 130, 7)
pyxel.show()
"
></pyxel-run>
```

To load an external file, specify `root` and `name`. `root` is the base directory for file lookup, and `name` is the file path.

```html
<pyxel-run root="." name="test.py"></pyxel-run>
```

If `root` is the current directory (`root="."`), the `root` attribute can be omitted.

### pyxel-play

A tag for running Pyxel applications (.pyxapp).

A URL can also be specified for `root`.

```html
<pyxel-play
  root="https://cdn.jsdelivr.net/gh/kitao/pyxel/python/pyxel/examples/apps"
  name="megaball.pyxapp"
></pyxel-play>
```

### pyxel-edit

A tag for editing resource files (.pyxres) in Pyxel Editor.

Use the `editor` attribute to specify which editor to launch.

```html
<pyxel-edit root="assets" name="shooter.pyxres" editor="image"></pyxel-edit>
```

### Common Attributes

The following common attributes are available for `pyxel-run` and `pyxel-play`.

| Attribute | Description |
| --- | --- |
| `gamepad="enabled"` | Enable the virtual gamepad (automatically displayed on touch devices) |

### Customizing Screen Display

By default, the Pyxel screen is displayed across the entire page. Place a `<div>` tag with `id="pyxel-screen"` in the HTML to render the screen inside that element, allowing you to adjust its position and size.

### Running in a Local Environment

When using HTML custom elements that load external files, a server is required because browser security restrictions prevent opening local files directly.

If you have a Python environment, you can start a simple server with the following command.

```sh
python -m http.server
# Use python3 on macOS and Linux
```

After starting the server, access `http://localhost:8000/test.html` in your browser.

---

--- METRICS ---
- Files Extracted: 11
- Estimated Token Budget: ~36918 tokens
- Canonical Reference: https://codewiki.google/github.com/kitao/pyxel
