tiramisu/asset

Asset Management System - loading and caching for textures, models, and audio.

Provides declarative asset loading with progress tracking, caching, and batch loading capabilities with LRU eviction.

Quick Example

import tiramisu/asset

// Load a texture
let load_effect = asset.load_texture("player.png")
  |> promise.map(fn(result) {
    case result {
      Ok(texture) -> TextureLoaded(texture)
      Error(err) -> LoadFailed(err)
    }
  })
  |> effect.from_promise

// Use with cache
let cache = asset.new_cache()
let cache = asset.insert_texture(cache, "player.png", texture)
let texture = asset.get_texture(cache, "player.png")

Types

Asset cache/registry with LRU eviction

pub opaque type AssetCache

Errors that can occur when working with the asset cache.

pub type AssetError {
  AssetLoadError(url: String, reason: String)
  AssetNotFound(url: String)
  InvalidAssetType(url: String)
}

Constructors

  • AssetLoadError(url: String, reason: String)

    Failed to load an asset with a specific reason

  • AssetNotFound(url: String)

    The requested asset was not found in the cache

  • InvalidAssetType(url: String)

    The cached asset exists but is the wrong type (e.g., requesting a texture when a model is cached)

Types of asset that can be loaded

pub type AssetType {
  ModelAsset(url: String)
  TextureAsset(url: String)
  AudioAsset(url: String)
  STLAsset(url: String)
  OBJAsset(obj_url: String, mtl_url: option.Option(String))
  FBXAsset(url: String, texture_path: option.Option(String))
  FontAsset(url: String)
}

Constructors

  • ModelAsset(url: String)

    GLTF/GLB 3D model

  • TextureAsset(url: String)

    Texture image (PNG, JPG, etc.)

  • AudioAsset(url: String)

    Audio file (MP3, WAV, OGG)

  • STLAsset(url: String)

    STL 3D model

  • OBJAsset(obj_url: String, mtl_url: option.Option(String))

    OBJ 3D model with optional MTL material file

    Supports loading Wavefront OBJ models with their associated MTL material files. The loader automatically:

    • Loads diffuse color maps (map_Kd)
    • Loads normal maps (map_bump) for surface detail
    • Loads ambient occlusion maps (map_Ka) for realistic shadows
    • Centers the model at origin
    • Computes vertex normals if missing

    MTL files can include texture paths with options (e.g., “map_bump -bm 1 texture.jpg”), which are properly parsed. Textures are assumed to be in the same directory as the MTL file.

  • FBXAsset(url: String, texture_path: option.Option(String))

    FBX 3D model with animations

    Supports loading Autodesk FBX models with embedded animations and materials. The FBX loader automatically:

    • Loads embedded textures and materials
    • Loads skeletal animations
    • Loads morph target animations
    • Computes vertex normals if missing
    • Handles both ASCII and binary FBX formats

    FBX files can contain multiple animations, which are returned as AnimationClips that can be used with the animation system.

    Texture Path: If textures are in a different directory than the FBX file, provide the texture_path to tell the loader where to find them. For example:

    • FBX: “assets/PSX_Dungeon/Models/Door.fbx”
    • Textures: “assets/PSX_Dungeon/Textures/”
    • Use: FBXAsset(url: "...", texture_path: Some("assets/PSX_Dungeon/Textures/"))
  • FontAsset(url: String)

    Font for TextGeometry (typeface.json format)

    Fonts must be in Three.js typeface.json format. You can convert TTF/OTF fonts using the facetype.js converter or download pre-converted fonts from the Three.js repository (examples/fonts/).

Result of batch loading

pub type BatchLoadResult {
  BatchLoadResult(cache: AssetCache, errors: List(AssetError))
}

Constructors

Opaque type for Three.js BufferGeometry.

Created by loading 3D models with asset.load_stl().

pub type BufferGeometry

Asset cache configuration.

Controls the maximum number of assets that can be cached before the least recently used asset is evicted.

pub type CacheConfig {
  CacheConfig(max_size: Int, current_time: Int)
}

Constructors

  • CacheConfig(max_size: Int, current_time: Int)

    Maximum number of assets before LRU eviction occurs

Data loaded from an FBX file.

Contains both the 3D scene and any embedded animations.

pub type FBXData {
  FBXData(
    scene: Object3D,
    animations: List(animation.AnimationClip),
  )
}

Constructors

  • FBXData(
      scene: Object3D,
      animations: List(animation.AnimationClip),
    )

    Arguments

    scene

    The root scene object containing all meshes and materials

    animations

    List of animation clips found in the FBX file

Opaque type for Three.js Font (for TextGeometry).

Created via asset.load_font() and used in text geometries. Fonts must be in typeface.json format.

pub type Font

Data loaded from a GLTF/GLB file.

Contains both the 3D scene and any embedded animations.

pub type GLTFData {
  GLTFData(
    scene: Object3D,
    animations: List(animation.AnimationClip),
  )
}

Constructors

  • GLTFData(
      scene: Object3D,
      animations: List(animation.AnimationClip),
    )

    Arguments

    scene

    The root scene object containing all meshes and materials

    animations

    List of animation clips found in the GLTF file

Errors that can occur when loading individual assets.

pub type LoadError {
  LoadError(String)
  InvalidUrl(String)
  ParseError(String)
}

Constructors

  • LoadError(String)

    General loading error with a message

  • InvalidUrl(String)

    The provided URL was invalid or empty

  • ParseError(String)

    The asset file could not be parsed

Progress information for batch loading.

Provided to the progress callback during load_batch().

pub type LoadProgress {
  LoadProgress(loaded: Int, total: Int, current_url: String)
}

Constructors

  • LoadProgress(loaded: Int, total: Int, current_url: String)

    Arguments

    loaded

    Number of assets successfully loaded so far

    total

    Total number of assets to load

    current_url

    URL of the asset currently being loaded

A loaded asset (opaque to enforce type safety)

pub opaque type LoadedAsset

Opaque type for Three.js Object3D.

Represents a 3D object loaded from GLTF, FBX, or OBJ files. Can be used in scene nodes or cloned for multiple instances.

pub type Object3D

Opaque type for Three.js textures.

Created via asset.load_texture() and used in materials.

pub type Texture

Texture filtering mode.

Controls how textures are sampled when they’re displayed at different sizes.

pub type TextureFilter {
  LinearFilter
  NearestFilter
}

Constructors

  • LinearFilter

    Linear filtering - smooth, blurred appearance (default in Three.js) Good for realistic graphics and smooth gradients

  • NearestFilter

    Nearest filtering - crisp, pixelated appearance Perfect for pixel art, low-poly aesthetics, and retro games

Values

pub fn apply_texture_to_object(
  object: Object3D,
  texture: Texture,
  filter: TextureFilter,
) -> Nil

Apply a texture to all materials in a loaded 3D model

pub fn cache_size(cache: AssetCache) -> Int

Get the number of assets currently in the cache.

Example

let cache = asset.new_cache()
// ... load some assets
let count = asset.cache_size(cache)  // => 5
pub fn cached_urls(cache: AssetCache) -> List(String)

Get all cached URLs

pub fn clear_cache(cache: AssetCache) -> AssetCache

Clear all cached assets.

This removes all assets from the cache but preserves the cache configuration. Useful for level transitions or when you need to free memory.

Example

// When transitioning to a new level
fn load_new_level(model: Model) {
  let cleared_cache = asset.clear_cache(model.assets)
  Model(..model, assets: cleared_cache)
}
pub fn clone_object3d(object: Object3D) -> Object3D

Clone an Object3D for reuse in multiple scene locations

When you need to place the same model in multiple locations, you must clone it. Three.js doesn’t allow the same Object3D to exist in multiple places in the scene graph.

Example

import tiramisu/asset

let assert Ok(fbx_data) = asset.get_fbx(cache, "models/tree.fbx")

// Create multiple instances of the tree
let tree1 = fbx_data.scene
let tree2 = asset.clone_object3d(fbx_data.scene)
let tree3 = asset.clone_object3d(fbx_data.scene)
pub fn get(
  cache: AssetCache,
  url: String,
) -> Result(LoadedAsset, AssetError)

Try to get any asset

pub fn get_audio(
  cache: AssetCache,
  url: String,
) -> Result(audio.AudioBuffer, AssetError)

Get an audio buffer from the cache

pub fn get_fbx(
  cache: AssetCache,
  url: String,
) -> Result(FBXData, AssetError)

Get an FBX model from the cache

Returns the loaded FBX model data including the scene object and animations. FBX files can contain:

  • Skeletal animations for character animation
  • Morph target animations
  • Embedded textures and materials
  • Multiple mesh objects in a scene hierarchy

Example

let assert Ok(character_data) = asset.get_fbx(cache, "models/character.fbx")

scene.Model3D(
  id: "character",
  object: character_data.scene,
  transform: transform.identity,
  animation: option.Some(
    animation.state_machine(character_data.animations)
  ),
  physics: option.None,
)
pub fn get_fbx_scene(
  cache: AssetCache,
  url: String,
) -> Result(Object3D, AssetError)

Get the scene object from a cached FBX model

pub fn get_model(
  cache: AssetCache,
  url: String,
) -> Result(GLTFData, AssetError)

Get a GLTF model from the cache (updates LRU timestamp)

pub fn get_model_scene(
  cache: AssetCache,
  url: String,
) -> Result(Object3D, AssetError)

Get the scene object from a cached GLTF model

pub fn get_obj(
  cache: AssetCache,
  url: String,
) -> Result(Object3D, AssetError)

Get an OBJ model from the cache

Returns the loaded OBJ model as an Object3D. The model will have:

  • Materials with textures applied (if MTL file was loaded)
  • Vertex normals computed
  • Center position at origin

Example

let assert Ok(bread_model) = asset.get_obj(cache, "models/bread.obj")

scene.Model3D(
  id: "bread",
  object: bread_model,
  transform: transform.identity,
  animation: option.None,
  physics: option.None,
)
pub fn get_stl(
  cache: AssetCache,
  url: String,
) -> Result(BufferGeometry, AssetError)

Get an STL geometry from the cache

pub fn get_texture(
  cache: AssetCache,
  url: String,
) -> Result(Texture, AssetError)

Get a texture from the cache

pub fn insert_asset(
  cache: AssetCache,
  url: String,
  asset: LoadedAsset,
) -> AssetCache

Insert a loaded asset into the cache manually If cache exceeds max_size, evicts least recently used asset

pub fn is_cached(cache: AssetCache, url: String) -> Bool

Check if a specific asset is cached.

Example

case asset.is_cached(cache, "textures/player.png") {
  True -> {
    // Asset is cached, get it immediately
    let assert Ok(texture) = asset.get_texture(cache, "textures/player.png")
    use_texture(texture)
  }
  False -> {
    // Need to load the asset first
    load_and_cache_texture("textures/player.png")
  }
}
pub fn load_asset(
  asset: AssetType,
) -> promise.Promise(Result(LoadedAsset, AssetError))

Load a single asset

pub fn load_audio(
  url: String,
) -> promise.Promise(Result(audio.AudioBuffer, LoadError))

Load an audio file from a URL using Promises

Supports common audio formats including MP3, WAV, and OGG. Returns an AudioBuffer that can be used with the audio system.

Example

import tiramisu/asset
import gleam/javascript/promise

let load_effect = asset.load_audio("sounds/jump.mp3")
  |> promise.map(fn(result) {
    case result {
      Ok(audio_buffer) -> AudioLoaded(audio_buffer)
      Error(err) -> LoadFailed(err)
    }
  })
pub fn load_batch(
  asset: List(AssetType),
  on_progress: fn(LoadProgress) -> Nil,
) -> promise.Promise(BatchLoadResult)

Load multiple asset with progress tracking Returns a promise that resolves with the loaded asset and any errors

pub fn load_batch_simple(
  asset: List(AssetType),
) -> promise.Promise(BatchLoadResult)

Load multiple asset without progress tracking

pub fn load_fbx(
  url: String,
  texture_path: String,
) -> promise.Promise(Result(FBXData, LoadError))

Load an FBX file with animations

Loads a 3D model in FBX format with full animation and material support. The loader handles:

  • Skeletal Animations: Bone-based character animations
  • Morph Target Animations: Vertex-based shape animations
  • Embedded Textures: Textures embedded in the FBX file
  • Materials: PBR and standard materials with properties
  • Scene Hierarchy: Complex object hierarchies preserved
  • Both Formats: ASCII and binary FBX files

Parameters

  • url: Path to the FBX file
  • texture_path: Optional path to texture directory (if textures are separate from FBX)

Returns

A Promise that resolves to:

  • Ok(FBXData): Loaded model with scene object and animation clips
  • Error(LoadError): File not found
  • Error(InvalidUrl): Invalid URL provided
  • Error(ParseError): Failed to parse FBX file

Example

import tiramisu/asset
import gleam/javascript/promise
import gleam/option

// FBX with textures in same directory
let load_effect = asset.load_fbx("models/character.fbx", "")
  |> promise.map(fn(result) {
    case result {
      Ok(fbx_data) -> ModelLoaded(fbx_data)
      Error(err) -> LoadFailed(err)
    }
  })

// FBX with textures in different directory
let load_effect = asset.load_fbx(
  "assets/PSX_Dungeon/Models/Door.fbx",
  "assets/PSX_Dungeon/Textures/"
)
pub fn load_font(
  url: String,
) -> promise.Promise(Result(Font, LoadError))

Load a font file (typeface.json format) for use with TextGeometry.

Three.js fonts must be in typeface.json format. You can:

  • Download pre-converted fonts from Three.js repository (examples/fonts/)
  • Convert TTF/OTF fonts using facetype.js converter

Example

import gleam/javascript/promise
import tiramisu/asset

let load_effect = asset.load_font("fonts/helvetiker_regular.typeface.json")
  |> promise.map(fn(result) {
    case result {
      Ok(font) -> FontLoaded(font)
      Error(err) -> LoadFailed(err)
    }
  })

Returns

A Promise that resolves to:

  • Ok(Font): Loaded font ready for TextGeometry
  • Error(LoadError): File not found
  • Error(InvalidUrl): Invalid URL provided
  • Error(ParseError): Failed to parse font file
pub fn load_gltf(
  url: String,
) -> promise.Promise(Result(GLTFData, LoadError))

Load a GLTF/GLB file from a URL using Promises

pub fn load_obj(
  obj_url obj_url: String,
  mtl_url mtl_url: String,
) -> promise.Promise(Result(Object3D, LoadError))

Load a Wavefront OBJ file with optional MTL materials

Loads a 3D model in OBJ format with full material and texture support. The loader handles:

  • Diffuse/Color Maps (map_Kd): Base color textures
  • Normal Maps (map_bump): Surface detail and lighting
  • Ambient Occlusion Maps (map_Ka): Contact shadows and depth
  • Vertex Normals: Computed automatically if missing
  • Model Centering: Centers the model at origin

Textures are loaded from the same directory as the MTL file. The loader properly parses MTL texture paths with options like map_bump -bm 1 normal.jpg.

Parameters

  • obj_url: Path to the OBJ file
  • mtl_url: Path to the MTL file, or empty string "" for no materials

Returns

A Promise that resolves to:

  • Ok(Object3D): Loaded model with materials and textures
  • Error(LoadError): File not found
  • Error(InvalidUrl): Invalid URL provided
  • Error(ParseError): Failed to parse OBJ/MTL file
pub fn load_stl(
  url: String,
) -> promise.Promise(Result(BufferGeometry, LoadError))

Load an STL file from a URL using Promises

pub fn load_texture(
  url: String,
) -> promise.Promise(Result(Texture, LoadError))

Load a texture from a URL using Promises

pub fn new_cache() -> AssetCache

Create a new empty asset cache with default max size.

The default maximum size is 100 assets. When this limit is exceeded, the least recently used asset is automatically evicted.

Example

import tiramisu/asset

let cache = asset.new_cache()
// Load and cache assets...
pub fn new_cache_with_size(max_size: Int) -> AssetCache

Create a new empty asset cache with a custom maximum size.

The max_size is the number of assets to cache before LRU eviction occurs.

Example

// Create a larger cache for games with many assets
let cache = asset.new_cache_with_size(500)

// Or a smaller cache for memory-constrained environments
let cache = asset.new_cache_with_size(20)
pub fn set_texture_filter(
  texture: Texture,
  filter: TextureFilter,
) -> Nil

Set the filtering mode for a texture

Search Document