Assets are files such as images, sounds, and 3D models. Use an asset key to tell Codea which file to load:
-- 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")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 projectasset.documents — your personal Documents folderasset.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:
-- 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"Use the standard reading functions with asset keys instead of file path strings:
-- 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 tableAsset keys also work with the file module for copying, moving, and deleting files.
Asset packs are folders that group related files together. You can inspect what’s inside a pack using pairs():
-- List all built-in Cargo Bot assets
for name, key in pairs(asset.builtin.Cargo_Bot) do
print(name, key)
endYou can create your own packs by creating subfolders inside your project.
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:
-- 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)
endRemove bookmarks when they’re no longer needed with assets.removeBookmark("myConfig").
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.
Download project Open in Codea to run this example.
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
Use 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.
Copying Files
The file.copy() function duplicates files and folders from one location to another:
-- 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(), or renamed within the same directory using file.rename():
-- 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(). The parent directory must exist before creating subdirectories:
-- 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() function deletes both files and directories:
-- Remove a single file
file.remove(asset.documents .. "TempFile.txt")
-- Remove a directory and all its contents
file.remove(asset.documents .. "TempFolder")Use these asset keys to choose where to read or write files:
-- 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")Check the result before using a file you have copied or a folder you have created:
local ok, err = file.copy(source, destination)
if not ok then
print("Operation failed:", err)
return
end
-- Continue with success caseUse file.exists() when an operation depends on whether a file or folder is already there:
-- 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")
endCopy or move a folder to include all its files and subfolders:
-- 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
endasset, asset.builtin, asset.documents, asset.icloud) cannot be removedCommon 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 issueRemove temporary files when you have finished with them:
-- 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
endSee Files 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.
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.
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.
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.
| source | assetKey or assetLibrary. Source file or folder to copy |
| destination | assetKey or assetLibrary. Destination path or folder |
boolean |
Success status and error message |
string or nil |
-- 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")
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.
| source | assetKey or assetLibrary. Source file or folder to move |
| destination | assetKey or assetLibrary. Destination path or folder |
boolean |
Success status and error message |
string or nil |
-- 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")
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.
| asset | assetKey or assetLibrary. File or folder to remove |
boolean |
Success status and error message |
string or nil |
-- 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)
file.mkdir(asset)
Creates a new directory. Fails if the directory already exists or if the parent directory does not exist.
| asset | assetKey or assetLibrary. Path where the directory should be created |
boolean |
Success status and error message |
string or nil |
-- 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")
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.
| asset | assetKey. Source file or folder to rename |
| newName | string. The new name for the file or folder |
boolean |
Success status and error message |
string or nil |
-- 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")
file.exists(asset)
Checks if a file or folder exists at the given path.
| asset | assetKey or assetLibrary. Path to check for existence |
boolean |
True if the path exists, false otherwise |
-- 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
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 itemUsage Patterns
-- 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
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:
function setup()
highScore = storage.highScore or 0
end
function recordScore(score)
highScore = math.max(highScore, score)
storage.highScore = highScore
endGroup 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:
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
endChoose 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:
-- 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.
storage.has(key)
Checks if a key exists.
| key | string. Key to check |
boolean |
True if the key exists, false otherwise |
if storage:has("playerName") then
print("Player name saved")
end
storage.delete(key)
Removes a key.
| key | string. Key to remove |
storage:delete("tutorialSeen")
storage.keys()
Returns a sorted array of all keys in the store.
table |
Sorted list of keys |
for _, key in ipairs(storage:keys()) do
print(key)
end
storage.clear()
Removes all keys from the store.
storage:clear()
storage.flush()
Writes any pending changes synchronously.
storage.level = 3
storage:flush()
storage.asset()
Returns the underlying asset key for the storage file.
assetKey |
Asset key used for the store file |
print(storage:asset())
storage.namespace(prefix)
Returns a view of the store that prefixes all keys with prefix. A . is added automatically if missing.
| prefix | string. Namespace prefix (for example, "prefs") |
table |
Namespaced storage view |
local prefs = storage:namespace("prefs")
prefs.audioMuted = true -- stored as "prefs.audioMuted"
print(prefs.audioMuted)
storage.project
Default project-scoped storage (stored under asset). This is the same store used by the top-level storage functions.
storage.global
Default device-wide storage (stored under asset.documents).
storage.projectStore(name)
Convenience wrapper for storage(name, "project").
| name | string. Store name |
table |
Storage instance |
storage.globalStore(name)
Convenience wrapper for storage(name, "global").
| name | string. Store name |
table |
Storage instance |
storage(name, scope)
Returns a named store. Stores are cached by name and scope, so repeated calls return the same store instance.
| name | string. Store name (used to derive the file name) |
| scope | string, optional. Store scope ("project" or "global") |
table |
Storage instance |
local settings = storage("settings")
local globalPrefs = storage("prefs", "global")
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.
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).
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
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 concept can be a short string, a longer piece of text, or an image.
text and an optional title lets Image Playground pick out the key ideas from a story or a noteimage or asset.key is used as a visual ideapick.playground {
concepts = {
"watercolor",
{ text = storyText, title = "The Lost Kite" },
asset.documents.Kite,
}
}
pick.image()
Opens the document picker to pick an image or PDF asset.
image |
The picked image or PDF asset |
myImage = pick.image()
...
sprite(myImage, WIDTH/2, HEIGHT/2)
pick.table()
Opens the document picker to pick a JSON asset and convert it to a table.
table |
The picked asset converted to a table |
myTable = pick.table()
...
print(myTable["key"])
pick.text()
Opens the document picker to pick a text asset.
string |
The text content of the picked asset |
myText = pick.text()
...
text(myText, WIDTH/2, HEIGHT/2)
pick.asset()
Opens the document picker to pick an asset and return its asset key.
asset.key |
The picked asset key |
myAssetKey = pick.asset()
...
print(myAssetKey.type)
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.
image |
The picked photo as an image asset |
myPhoto = pick.photo()
...
sprite(myPhoto, WIDTH/2, HEIGHT/2)
pick.sound()
Opens the document picker to pick an audio asset (sound or music).
sound.source |
The picked audio asset |
sound.play(pick.sound())
pick(...)
| ... | string or integer or function. UTType strings, pick.option values, and an optional callback receiving the selected assets. Order is flexible. |
pick.option
A table containing the following options:
text - Text assetjson - JSON assetsound - Audio asset (sound or music)pdf - PDF assetimage - 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 selectionassetKey - Return the asset key instead of the asset contentdecodeTable - 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 filepick.playground(concepts, options, callback)
| 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 |
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),
}
pick.playground.available
true if Image Playground can be shown on this device
pick.playground.style
any - lets Image Playground choose (iOS 27)animation - 3D animated lookillustration - flat, bold illustrationsketch - hand-drawn sketchemoji - emoji-like lookexternal - an external provider such as ChatGPT, if the user has turned one on (iOS 26)pick.playground.personalization
automatic - let Image Playground decideenabled - allow people from the photo librarydisabled - never suggest people from the photo librarypick.playground.variety
automatic, high, low (iOS 26.4)pick.playground.strategy
automatic - let Image Playground decideedit - change the source imagegenerate - make a new image based on itRequires iOS 27
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.
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)
endUse storage to persist ordinary Lua values without encoding them yourself. Use JSON when a file format or web service requires it.
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.
| value | any. Boolean, number, string, table, or json.null to encode. |
| options | table, optional. Optional encoder settings, such as {indent = true}. |
string |
Encoded JSON text. |
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.
| 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. |
any |
Decoded value, next byte position, and an error string or nil. |
json.null
Sentinel representing JSON null. It can be encoded and supplied to decode to distinguish a null value from an absent table entry.