# Storage & Assets

Managing files, assets and persistent data

[Reference](index.md)

<a id="section-Guides"></a>

## Guides

<a id="Assets-in-Codea"></a>

### Assets in Codea

Assets are files such as images, sounds, and 3D models. Use an *asset key* to tell Codea which file to load:

```lua
-- Load an image from the built-in Codea assets
myImage = readImage(asset.builtin.Cargo_Bot.Codea_Dark)

-- Load from your own project
myImage = readImage(asset .. "MySprite.png")
```

## Asset Keys

An asset key identifies a file or folder. For example, `asset.myImage` refers to an image in your project.

The main asset libraries are:

- `asset` — files in your current project
- `asset.documents` — your personal Documents folder
- `asset.builtin` — read-only Codea built-in assets (sprites, sounds, shaders)
- `asset.icloud` — iCloud Drive documents (when signed in)

You can navigate sub-folders using dot notation or the `..` operator:

```lua
-- Dot notation (for known names)
local key = asset.builtin.Cargo_Bot.Codea_Dark

-- Concatenation (for dynamic names)
local filename = "MySprite.png"
local key = asset .. filename

-- Combine paths
local key = asset.documents .. "Saves/Level1.json"
```

## Loading and Saving Assets

Use the standard reading functions with asset keys instead of file path strings:

```lua
-- Images
local img = readImage(asset.builtin.Cargo_Bot.Codea_Dark)
saveImage(asset.documents .. "Screenshot.png", img)

-- Text
local text = readLocalData("highscore")
saveLocalData("highscore", 1000)

-- JSON via the pick API
local tbl = pick.table()   -- opens document picker, returns a table
```

Asset keys also work with the [`file`](#file) module for copying, moving, and deleting files.

## Working with Asset Packs

Asset packs are folders that group related files together. You can inspect what’s inside a pack using `pairs()`:

```lua
-- List all built-in Cargo Bot assets
for name, key in pairs(asset.builtin.Cargo_Bot) do
    print(name, key)
end
```

You can create your own packs by creating subfolders inside your project.

## Bookmarks (References)

When you pick a file from outside your project using `pick.option.reference`, you receive an asset key that points to the original file. Because the file path may change between sessions, you must save a *bookmark* to reliably access it later:

```lua
-- Pick a file by reference and save a bookmark
local key = pick.asset(pick.option.reference)
if key then
    key:saveBookmark("myConfig")
end

-- On a later run, restore the bookmark
local key = assets.readBookmark("myConfig")
if key then
    local text = read(key)
end
```

Remove bookmarks when they’re no longer needed with `assets.removeBookmark("myConfig")`.

## Drawing many sprites from one image

Create a ring image once in `setup()`, then draw it in many moving positions. Try using `image.read(asset.myImage)` to draw your own picture instead. Passing a width without a height keeps the image proportions.

#### Drawing many sprites from one image

[Download project](data/downloads/Drawing-many-sprites-from-one-image-fbff45b2e264.zip) — Open in Codea to run this example.

```lua
local icon
local spr = sprite

function setup()
    -- Generate a local image so this example needs no installed asset packs.
    icon = image(64, 64)
    context.push(icon)
    background(0, 0, 0, 0)
    style.fill(100, 220, 200).noStroke()
    ellipse(32, 32, 56)
    fill(25)
    ellipse(32, 32, 20)
    context.pop()
end

function draw()
    background(25, 25, 25)

    -- The sprite command draws an image on the screen for us

    -- Sprites can come from two different places:
    -- Asset Keys, which are paths to assets on your device, including built-in assets, project assets and documents assets
    -- Images, which are loaded or generated somewhere else in the code via image.read(key) or image(w,h)

    -- The basic command is: sprite(imageOrAsset, x, y, w, h) where you specify the x and y position and optional width and height
    -- Specifying only the width will scale the sprite's height correctly to match aspect ratio
    --style.blend(DISABLED)
    local t = time.elapsed
    for i = 0, 24 do
        sprite(icon, WIDTH/2 + math.sin(t + i) * 100, HEIGHT/2 + math.cos(t + i) * 100, 32)
    end
end

function touched(touch)
end

```

<a id="Working-with-Files-in-Codea"></a>

### Working with Files in Codea

Use [`file`](#file) to copy, move, rename, and delete files, or create folders. Give each operation an asset key for the file or folder you want to work with.

These operations return `true, nil` on success, or `false` and an error message on failure.

## Basic File Operations

**Copying Files**

The [`file.copy()`](#file.copy) function duplicates files and folders from one location to another:

```lua
-- Copy a file with a new name
local ok, err = file.copy(asset.Main, asset.documents .. "MainBackup.lua")
if not ok then
    print("Copy failed:", err)
end

-- Copy a file to a directory (keeps original name)
file.copy(asset.builtin.Platformer_Art.Player_01, asset.documents)
```

When copying to an asset library (a folder), the file automatically maintains its original filename in the destination.

**Moving and Renaming Files**

Files can be moved between locations using [`file.move()`](#file.move), or renamed within the same directory using [`file.rename()`](#file.rename):

```lua
-- Move a file to a new location
file.move(asset.documents .. "OldLocation.lua", asset.documents .. "NewLocation.lua")

-- Rename a file within the same directory
file.rename(asset.documents .. "OldName.lua", "NewName.lua")
```

**Creating Directories**

New directories are created with [`file.mkdir()`](#file.mkdir). The parent directory must exist before creating subdirectories:

```lua
-- Create a screenshots directory
file.mkdir(asset.documents .. "Screenshots")

-- Create nested directories (parent must exist first)
file.mkdir(asset.documents .. "Projects")
file.mkdir(asset.documents .. "Projects/GameData")
```

**Removing Files and Directories**

The [`file.remove()`](#file.remove) function deletes both files and directories:

```lua
-- Remove a single file
file.remove(asset.documents .. "TempFile.txt")

-- Remove a directory and all its contents
file.remove(asset.documents .. "TempFolder")
```

## Choosing a location

Use these asset keys to choose where to read or write files:

- **asset** - Your current project files
- **asset.documents** - User documents folder
- **asset.builtin** - Built-in Codea assets (read-only)
- **asset.icloud** - iCloud documents (when available)

```lua
-- Copy from builtin assets to your project
file.copy(asset.builtin.Planet_Cute.Heart, asset .. "Heart.png")

-- Organize project files
file.mkdir(asset .. "Images")
file.move(asset .. "Heart.png", asset .. "Images/Heart.png")
```

## Checking for errors

Check the result before using a file you have copied or a folder you have created:

```lua
local ok, err = file.copy(source, destination)
if not ok then
    print("Operation failed:", err)
    return
end
-- Continue with success case
```

Use `file.exists()` when an operation depends on whether a file or folder is already there:

```lua
-- Only create directory if it doesn't exist
if not file.exists(asset.documents .. "Cache") then
    local ok, err = file.mkdir(asset.documents .. "Cache")
    if not ok then
        print("Failed to create cache directory:", err)
    end
end

-- Safe cleanup - remove only if exists
if file.exists(asset.documents .. "TempFile.txt") then
    file.remove(asset.documents .. "TempFile.txt")
end
```

## Working with Folders

Copy or move a folder to include all its files and subfolders:

```lua
-- Copy an entire asset pack to documents
file.copy(asset.builtin.Space_Art, asset.documents .. "SpaceAssets")

-- Reorganize project structure
file.mkdir(asset .. "Source")
file.mkdir(asset .. "Assets")

-- Move Lua files to Source folder
for _, fileName in ipairs({"Main.lua", "Game.lua", "Menu.lua"}) do
    if file.exists(asset .. fileName) then
        file.move(asset .. fileName, asset .. "Source/" .. fileName)
    end
end
```

## Protected locations

- Top-level asset packs (`asset`, `asset.builtin`, `asset.documents`, `asset.icloud`) cannot be removed
- Built-in assets are read-only and cannot be modified
- Operations that would overwrite existing files fail by default

**Common Error Conditions**

- `"No such file or directory."` - Source doesn’t exist
- `"Destination already exists. Remove it first."` - Target already exists
- `"Cannot remove built-in or top-level asset packs."` - Protected location
- `"Parent folder does not exist."` - Directory structure issue

Remove temporary files when you have finished with them:

```lua
-- Example: Safe project backup function
function backupProject()
    local timestamp = os.date("%Y%m%d_%H%M%S")
    local backupDir = asset.documents .. "Backups/Project_" .. timestamp

    -- Create backup directory
    local ok, err = file.mkdir(asset.documents .. "Backups")
    if not ok and not string.find(err, "already exists") then
        print("Failed to create backup folder:", err)
        return false
    end

    ok, err = file.mkdir(backupDir)
    if not ok then
        print("Failed to create timestamped backup:", err)
        return false
    end

    -- Copy all project files
    ok, err = file.copy(asset, backupDir)
    if not ok then
        print("Failed to backup project:", err)
        return false
    end

    print("Project backed up to:", backupDir)
    return true
end
```

See [Files](#file) for the complete reference.
Reading and writing text
————————-

Use an asset key’s coordinated access when reading or writing text files. This
lets Codea coordinate access to files stored in iCloud or a document provider.
Open and close the file inside the callback, rather than keeping its path or
file handle for later.

```lua
local key = asset.documents .. "Notes.txt"
key:coordinateSave(function(path)
    local handle, errorMessage = io.open(path, "w")
    if not handle then print(errorMessage); return end
    handle:write("Hello from Codea!\n")
    handle:close()
end)

key:coordinateRead(function(path)
    local handle, errorMessage = io.open(path, "r")
    if not handle then print(errorMessage); return end
    local contents = handle:read("*a")
    handle:close()
    print(contents)
end)
```

For structured files, write `json.encode(value)` and read them with
`json.decode(contents)`. For project preferences and scores, `storage` is
simpler and handles persistence for you.

<a id="section-Files"></a>

## Files

<a id="file"></a>

### file

Copy, move, rename, and delete files or create folders using asset keys. These operations return `true, nil` on success, or `false` and an error message on failure.

<a id="file.copy"></a>

### file.copy(source, destination)

```lua
file.copy(source, destination)
```

Copies a file or folder from one location to another. When copying to an asset library (folder), the file maintains its original name in the destination. Fails if the source does not exist or if the destination already exists.

copy a file or folder

#### Parameters

- **source** (assetKey or assetLibrary): Source file or folder to copy
- **destination** (assetKey or assetLibrary): Destination path or folder

#### Returns

- **boolean**: Success status and error message
- **string or nil**: 

#### Example

```lua
-- Copy a file to documents with a new name
local ok, err = file.copy(asset.builtin.Pack.File, asset.documents .. "MyFile.png")
if not ok then
    print("Copy failed:", err)
end

-- Copy a file to documents (keeps original name)
local ok, err = file.copy(asset.builtin.Pack.File, asset.documents)

-- Copy an entire folder
local ok, err = file.copy(asset.builtin.Pack, asset.documents .. "MyPack")
```

<a id="file.move"></a>

### file.move(source, destination)

```lua
file.move(source, destination)
```

Moves a file or folder from one location to another. When moving to an asset library (folder), the file maintains its original name in the destination. Fails if the source does not exist or if the destination already exists.

move a file or folder

#### Parameters

- **source** (assetKey or assetLibrary): Source file or folder to move
- **destination** (assetKey or assetLibrary): Destination path or folder

#### Returns

- **boolean**: Success status and error message
- **string or nil**: 

#### Example

```lua
-- Move a file to documents with a new name
local ok, err = file.move(asset.icloud.Pack.File, asset.documents .. "MyFile.png")
if not ok then
    print("Move failed:", err)
end

-- Move a file to documents (keeps original name)
local ok, err = file.move(asset.icloud.Pack.File, asset.documents)

-- Move an entire folder
local ok, err = file.move(asset.icloud.Pack, asset.documents .. "MyPack")
```

<a id="file.remove"></a>

### file.remove(asset)

```lua
file.remove(asset)
```

Removes a file or folder. Built-in folders and top-level asset packs (asset, asset.builtin, asset.documents, asset.icloud) cannot be removed. Fails if the file does not exist.

remove a file or folder

#### Parameters

- **asset** (assetKey or assetLibrary): File or folder to remove

#### Returns

- **boolean**: Success status and error message
- **string or nil**: 

#### Example

```lua
-- Remove a file
local ok, err = file.remove(asset.documents.File)
if not ok then
    print("Remove failed:", err)
end

-- Remove a folder and all its contents
local ok, err = file.remove(asset.documents.SomeFolder)
```

<a id="file.mkdir"></a>

### file.mkdir(asset)

```lua
file.mkdir(asset)
```

Creates a new directory. Fails if the directory already exists or if the parent directory does not exist.

create a new folder

#### Parameters

- **asset** (assetKey or assetLibrary): Path where the directory should be created

#### Returns

- **boolean**: Success status and error message
- **string or nil**: 

#### Example

```lua
-- Create a new folder in documents
local ok, err = file.mkdir(asset.documents .. "MyFolder")
if not ok then
    print("Create directory failed:", err)
end

-- Will fail if parent doesn't exist
local ok, err = file.mkdir(asset.documents .. "Parent/Child")
-- First create parent, then child
file.mkdir(asset.documents .. "Parent")
file.mkdir(asset.documents .. "Parent/Child")
```

<a id="file.rename"></a>

### file.rename(asset, newName)

```lua
file.rename(asset, newName)
```

Renames a file or folder within the same parent directory. This is a synonym for moving within the same parent folder to a new name. Fails if the source does not exist or a sibling with the new name already exists.

rename a file or folder

#### Parameters

- **asset** (assetKey): Source file or folder to rename
- **newName** (string): The new name for the file or folder

#### Returns

- **boolean**: Success status and error message
- **string or nil**: 

#### Example

```lua
-- Rename a file within documents
local ok, err = file.rename(asset.documents .. "Old.png", "New.png")
if not ok then
    print("Rename failed:", err)
end

-- Rename a folder within documents
local ok, err = file.rename(asset.documents .. "OldFolder", "NewFolder")
```

<a id="file.exists"></a>

### file.exists(asset)

```lua
file.exists(asset)
```

Checks if a file or folder exists at the given path.

check if a file or folder exists

#### Parameters

- **asset** (assetKey or assetLibrary): Path to check for existence

#### Returns

- **boolean**: True if the path exists, false otherwise

#### Example

```lua
-- Check if a configuration file exists
if file.exists(asset.documents .. "Config.json") then
    print("Config is present")
else
    print("Missing Config.json")
end

-- Check if a folder exists
if file.exists(asset.documents .. "Screenshots") then
    print("Screenshots folder exists")
end
```

<a id="File-Errors-and-Usage-Patterns"></a>

### File Errors and Usage Patterns

The file operations return specific error messages for different failure conditions:

- `"No such file or directory."` - The source file or folder does not exist
- `"Destination already exists. Remove it first."` - The destination already exists and operations don’t overwrite
- `"Cannot remove built-in or top-level asset packs."` - Attempting to remove protected system folders
- `"Parent folder does not exist."` - Parent directory must exist when creating folders or copying files
- `"Cannot move item: source and destination are identical."` - Source and destination paths are the same
- `"A file or directory with that name already exists."` - Directory creation failed due to existing item

**Usage Patterns**

```lua
-- Helper function for error checking
local function checkResult(ok, err, operation)
    if not ok then
        print(operation .. " failed:", err)
        return false
    end
    return true
end

-- Example usage with error handling
local ok, err = file.copy(asset.Main, asset.documents .. "Backup.lua")
if checkResult(ok, err, "Backup creation") then
    print("Backup created successfully")
end

-- Verify operations with file.exists
if file.exists(asset.documents .. "Backup.lua") then
    local ok, err = file.remove(asset.documents .. "Backup.lua")
    checkResult(ok, err, "Cleanup")
end
```

<a id="section-Storage"></a>

## Storage

<a id="storage"></a>

### storage

Use `storage` to keep settings, high scores, and progress between runs. Read and write values by name, such as `storage.highScore`. Values belong to the current project by default; `storage.project` refers to the same store.

**Load a default, then save when something changes**

Read saved values in `setup()`, choosing a default if nothing is saved yet. Save when a value changes. Here, `recordScore(score)` saves the high score at the end of a game:

```lua
function setup()
    highScore = storage.highScore or 0
end

function recordScore(score)
    highScore = math.max(highScore, score)
    storage.highScore = highScore
end
```

**Group related values with namespaces**

A namespace groups related keys in the same store. Here, `prefs.musicEnabled` uses the key `"prefs.musicEnabled"`. Check for `nil` when choosing a default so a saved `false` value is kept:

```lua
local prefs = storage:namespace("prefs")
if prefs.musicEnabled == nil then
    prefs.musicEnabled = true -- default only on first use
end

local stats = storage:namespace("stats")
-- Call this when a game finishes, not every frame.
function recordGamePlayed()
    stats.gamesPlayed = (stats.gamesPlayed or 0) + 1
end
```

**Choose project-local or shared data**

Use the default store for one project’s progress. Use `storage.global` to share values across projects on the same device. These values do not sync between devices:

```lua
-- Private to this project
storage.level = 3

-- Shared by projects using this namespace on this device
local sharedPrefs = storage.global:namespace("myGames")
sharedPrefs.theme = "dark"
```

**Supported values and saving**

Values can be booleans, numbers, strings, or tables containing those kinds of values. Assign `nil` to delete a key, for example `storage.temp = nil`. Use assets and files for images or other file contents.

Writes are saved in the background. Call `storage:flush()` when you need pending changes written before continuing. For a different store, flush that store, such as `storage.global:flush()`; there is no need to flush every frame.

<a id="storage.has"></a>

### storage.has(key)

```lua
storage.has(key)
```

Checks if a key exists.

check if a key exists

#### Parameters

- **key** (string): Key to check

#### Returns

- **boolean**: True if the key exists, false otherwise

#### Example

```lua
if storage:has("playerName") then
    print("Player name saved")
end
```

<a id="storage.delete"></a>

### storage.delete(key)

```lua
storage.delete(key)
```

Removes a key.

remove a key from storage

#### Parameters

- **key** (string): Key to remove

#### Example

```lua
storage:delete("tutorialSeen")
```

<a id="storage.keys"></a>

### storage.keys()

```lua
storage.keys()
```

Returns a sorted array of all keys in the store.

get all keys in storage

#### Returns

- **table**: Sorted list of keys

#### Example

```lua
for _, key in ipairs(storage:keys()) do
    print(key)
end
```

<a id="storage.clear"></a>

### storage.clear()

```lua
storage.clear()
```

Removes all keys from the store.

remove all keys from storage

#### Example

```lua
storage:clear()
```

<a id="storage.flush"></a>

### storage.flush()

```lua
storage.flush()
```

Writes any pending changes synchronously.

write pending storage changes immediately

#### Example

```lua
storage.level = 3
storage:flush()
```

<a id="storage.asset"></a>

### storage.asset()

```lua
storage.asset()
```

Returns the underlying asset key for the storage file.

get the asset key used by this store

#### Returns

- **assetKey**: Asset key used for the store file

#### Example

```lua
print(storage:asset())
```

<a id="storage.namespace"></a>

### storage.namespace(prefix)

```lua
storage.namespace(prefix)
```

Returns a view of the store that prefixes all keys with `prefix`. A `.` is added automatically if missing.

create a namespaced view of storage

#### Parameters

- **prefix** (string): Namespace prefix (for example, "prefs")

#### Returns

- **table**: Namespaced storage view

#### Example

```lua
local prefs = storage:namespace("prefs")
prefs.audioMuted = true -- stored as "prefs.audioMuted"
print(prefs.audioMuted)
```

<a id="storage.project"></a>

### storage.project

```lua
storage.project
```

Default project-scoped storage (stored under `asset`). This is the same store used by the top-level `storage` functions.

default project storage

<a id="storage.global"></a>

### storage.global

```lua
storage.global
```

Default device-wide storage (stored under `asset.documents`).

default device-wide storage

<a id="storage.projectStore"></a>

### storage.projectStore(name)

```lua
storage.projectStore(name)
```

Convenience wrapper for `storage(name, "project")`.

create or get a named project store

#### Parameters

- **name** (string): Store name

#### Returns

- **table**: Storage instance

<a id="storage.globalStore"></a>

### storage.globalStore(name)

```lua
storage.globalStore(name)
```

Convenience wrapper for `storage(name, "global")`.

create or get a named global store

#### Parameters

- **name** (string): Store name

#### Returns

- **table**: Storage instance

<a id="storage-2"></a>

### storage(name, scope)

```lua
storage(name, scope)
```

Returns a named store. Stores are cached by name and scope, so repeated calls return the same store instance.

create or get a named storage object

#### Parameters

- **name** (string): Store name (used to derive the file name)
- **scope** (string, optional): Store scope ("project" or "global")

#### Returns

- **table**: Storage instance

#### Example

```lua
local settings = storage("settings")
local globalPrefs = storage("prefs", "global")
```

<a id="section-Asset-Picking"></a>

## Asset Picking

<a id="Picking-Assets"></a>

### Picking Assets

Let the user choose a file or photo with the system picker.

Documents are copied into your project by default. Use `pick.option.reference` to keep a reference to the original file instead.

Images are always copied to the project’s assets and cannot be loaded as references.

<a id="Picking-by-Reference"></a>

### Picking by Reference

When using `pick.option.reference`, the picked asset is not copied to the project’s assets and instead points to the original file.

This allows you to make updates to the original file.

However, you cannot store the path to the file (e.g. using your `assetKey.path`) as the path is not guaranteed to be the same on subsequent runs.

If you need to store the path, you must save and read bookmarks using `assetKey:saveBookmark(name)` and `assets.readBookmark(name)`.

Bookmarks can be removed using `assets.removeBookmark(name)`.

```lua
local assetKey = pick.asset(pick.option.reference)
if assetKey then
    assetKey:saveBookmark("myBookmark")
end

-- On subsequent runs, read the bookmark
local assetKey = assets.readBookmark("myBookmark")
if assetKey then
    print(assetKey.path)
end
```

<a id="Generating-Images"></a>

### Generating Images

`pick.playground` brings up Image Playground so you can describe an image, choose a style and pick one of the results. Like the other pickers, it waits for the user and returns the image, or returns right away when you give it a callback.

The chosen image is copied to your project’s assets, like `pick.photo`.

Image Playground needs a device with Apple Intelligence turned on. Check `pick.playground.available` before offering it.

<a id="Describing-Generated-Images"></a>

### Describing Generated Images

A concept can be a short string, a longer piece of text, or an image.

- A string is used as it is
- A table with `text` and an optional `title` lets Image Playground pick out the key ideas from a story or a note
- An `image` or `asset.key` is used as a visual idea

```lua
pick.playground {
    concepts = {
        "watercolor",
        { text = storyText, title = "The Lost Kite" },
        asset.documents.Kite,
    }
}
```

<a id="section-Picking-Assets"></a>

## Picking Assets

<a id="pick"></a>

### pick()

```lua
pick()
```

Opens the document picker to pick a single asset and convert it to its corresponding Codea asset type.

open the document picker to pick an asset

#### Returns

- **any**: The picked asset

#### Example

```lua
local pickedAsset = pick()
if pickedAsset then
    print(pickedAsset)
end
```

<a id="section-Picking-Specific-Types"></a>

## Picking Specific Types

<a id="pick.image"></a>

### pick.image()

```lua
pick.image()
```

Opens the document picker to pick an image or PDF asset.

open the document picker to pick an image

#### Returns

- **image**: The picked image or PDF asset

#### Example

```lua
myImage = pick.image()
...
sprite(myImage, WIDTH/2, HEIGHT/2)
```

<a id="pick.table"></a>

### pick.table()

```lua
pick.table()
```

Opens the document picker to pick a JSON asset and convert it to a table.

open the document picker to pick a JSON asset as table

#### Returns

- **table**: The picked asset converted to a table

#### Example

```lua
myTable = pick.table()
...
print(myTable["key"])
```

<a id="pick.text"></a>

### pick.text()

```lua
pick.text()
```

Opens the document picker to pick a text asset.

open the document picker to pick a text asset

#### Returns

- **string**: The text content of the picked asset

#### Example

```lua
myText = pick.text()
...
text(myText, WIDTH/2, HEIGHT/2)
```

<a id="pick.asset"></a>

### pick.asset()

```lua
pick.asset()
```

Opens the document picker to pick an asset and return its asset key.

open the document picker to pick an asset key

#### Returns

- **asset.key**: The picked asset key

#### Example

```lua
myAssetKey = pick.asset()
...
print(myAssetKey.type)
```

<a id="pick.photo"></a>

### pick.photo()

```lua
pick.photo()
```

Opens the photo picker to pick a single photo from the photo library.

This is a different picker than the document picker, and only allows picking a single photo at a time.

open the photo picker to pick a photo

#### Returns

- **image**: The picked photo as an image asset

#### Example

```lua
myPhoto = pick.photo()
...
sprite(myPhoto, WIDTH/2, HEIGHT/2)
```

<a id="pick.sound"></a>

### pick.sound()

```lua
pick.sound()
```

Opens the document picker to pick an audio asset (sound or music).

open the document picker to pick an audio asset

#### Returns

- **sound.source**: The picked audio asset

#### Example

```lua
sound.play(pick.sound())
```

<a id="section-Advanced-Usage"></a>

## Advanced Usage

<a id="pick-2"></a>

### pick(...)

```lua
pick(...)
```

pick assets with specified types and options

#### Parameters

- **...** (string or integer or function): UTType strings, pick.option values, and an optional callback receiving the selected assets. Order is flexible.

<a id="entry.pick.option"></a>

### pick.option

```lua
pick.option
```

A table containing the following options:

- `text` - Text asset
- `json` - JSON asset
- `sound` - Audio asset (sound or music)
- `pdf` - PDF asset
- `image` - Image or PDF asset, defined as `{ "public.image", "com.adobe.pdf" }`
- `table` - JSON asset converted to a table, defined as `{ pick.option.json, pick.option.decodeTable }`
- `multiple` - Enable multiple asset selection
- `assetKey` - Return the asset key instead of the asset content
- `decodeTable` - Decode the picked asset as a table (only for json assets)
- `reference` - Do not copy the asset to the project’s assets, instead reference the original file

picker options table

<a id="section-Generating-Images"></a>

## Generating Images

<a id="pick.playground"></a>

### pick.playground(concepts, options, callback)

```lua
pick.playground(concepts, options, callback)
```

open Image Playground to generate an image

#### Parameters

- **concepts** (string, optional): A short description to start with
- **options** (table, optional): A table of the settings below
- **callback** (function, optional): Called with the chosen image. Makes the call asynchronous

#### Turning a photo into a sketch

```lua
local photo = pick.photo()
local result = pick.playground {
    source = photo,
    strategy = pick.playground.strategy.edit,
    style = pick.playground.style.sketch,
    styles = { pick.playground.style.sketch },
    size = vec2(1024, 1024),
}
```

<a id="entry.pick.playground.available"></a>

### pick.playground.available

```lua
pick.playground.available
```

`true` if Image Playground can be shown on this device

whether Image Playground is available

<a id="entry.pick.playground.style"></a>

### pick.playground.style

```lua
pick.playground.style
```

- `any` - lets Image Playground choose (iOS 27)
- `animation` - 3D animated look
- `illustration` - flat, bold illustration
- `sketch` - hand-drawn sketch
- `emoji` - emoji-like look
- `external` - an external provider such as ChatGPT, if the user has turned one on (iOS 26)

Image Playground styles

<a id="entry.pick.playground.personalization"></a>

### pick.playground.personalization

```lua
pick.playground.personalization
```

- `automatic` - let Image Playground decide
- `enabled` - allow people from the photo library
- `disabled` - never suggest people from the photo library

Image Playground personalization settings

<a id="entry.pick.playground.variety"></a>

### pick.playground.variety

```lua
pick.playground.variety
```

- `automatic`, `high`, `low` (iOS 26.4)

how different Image Playground results are

<a id="entry.pick.playground.strategy"></a>

### pick.playground.strategy

```lua
pick.playground.strategy
```

- `automatic` - let Image Playground decide
- `edit` - change the source image
- `generate` - make a new image based on it

Requires iOS 27

whether Image Playground edits or replaces the source

<a id="section-JSON"></a>

## JSON

<a id="json"></a>

### json

JSON exchanges structured data with web services and files. Lua tables become
JSON objects or arrays; JSON objects and arrays become Lua tables. Functions,
userdata, and cyclic tables cannot be encoded directly: convert values such
as colors or vectors into ordinary tables first.

```lua
local encoded = json.encode({name = "Ada", score = 42})
local data, nextPosition, errorMessage = json.decode(encoded, 1, json.null)
if errorMessage then
    print("Invalid JSON:", errorMessage)
else
    print(data.name, data.score)
end
```

Use `storage` to persist ordinary Lua values without encoding them yourself.
Use JSON when a file format or web service requires it.

<a id="json.encode"></a>

### json.encode(value, options)

```lua
json.encode(value, options)
```

Returns a JSON string. Sequential numeric table keys form an array; other
tables form objects. Encoding an unsupported value raises an error; use
`pcall(json.encode, value)` when handling untrusted structures.
`options.indent = true` produces readable indented output.

encode a Lua value as JSON

#### Parameters

- **value** (any): Boolean, number, string, table, or json.null to encode.
- **options** (table, optional): Optional encoder settings, such as {indent = true}.

#### Returns

- **string**: Encoded JSON text.

<a id="json.decode"></a>

### json.decode(text, position, nullValue)

```lua
json.decode(text, position, nullValue)
```

Returns `value, nextPosition, errorMessage`. On failure, `value` is nil
and `errorMessage` describes the error. By default JSON null becomes nil;
pass `json.null` as the third argument to preserve null entries in arrays
and objects. The next position allows reading another value after this one.

decode JSON into Lua values

#### Parameters

- **text** (string): JSON text to parse.
- **position** (integer, optional): Optional starting byte position, default 1.
- **nullValue** (any, optional): Value to use for JSON null; normally json.null.

#### Returns

- **any**: Decoded value, next byte position, and an error string or nil.

<a id="json.null"></a>

### json.null

```lua
json.null
```

Sentinel representing JSON null. It can be encoded and supplied to `decode`
to distinguish a null value from an absent table entry.

represent an explicit JSON null value
