Skip to main content
Assets are the files your game uses at runtime — models, materials, sounds, textures, and custom data types. s&box gives you a built-in Asset Browser to find and manage these files, a set of virtual file systems for reading and writing data, and a straightforward API for loading assets in code.

What counts as an asset

Models

3D geometry files (.vmdl) for characters, props, and world objects.

Materials

Surface definitions (.vmat) that describe how geometry is shaded.

Sounds

Audio files and sound events (.vsnd) for music, SFX, and voice.

Custom assets

Your own GameResource types with any properties you define.

The Asset Browser

The Asset Browser is the in-editor panel for finding and working with assets. Enable Show Base Content in its settings to make the platform’s built-in content (the citizen addon folder and other core files) visible alongside your project files.
Never add or modify files inside the core addon folders (for example, the citizen addon). Only work inside your own project folder.

Cloud Browser

The Cloud Browser lets you drag any asset from sbox.game directly into your scene. s&box downloads and caches the files in the background so you do not need to manage them manually.

Loading assets in code

Loading by path

Use the static Load method on the relevant type to load an asset from a path. Paths are relative to the mounted file system.
// Load a model
var model = Model.Load( "models/citizen/citizen.vmdl" );

// Load a material
var mat = Material.Load( "materials/dev/reflectivity_30.vmat" );

// Load a sound
var sound = Sound.Load( "sounds/player/footstep.vsnd" );

Referencing cloud assets by ident

If you know a cloud asset’s ident on sbox.game, use the Cloud.* helpers. The asset is downloaded at compile time and shipped with your package.
var model = Cloud.Model( "facepunch.w_usp" );        // dot notation
var model2 = Cloud.Model( "facepunch/w_usp" );       // slash notation
var model3 = Cloud.Model( "https://sbox.game/facepunch/w_usp" ); // full URL

Mounting cloud assets at runtime

For games where players can spawn arbitrary content (like a sandbox), you can download and mount packages at runtime.
static async Task<Model> DownloadModel( string packageIdent )
{
    var package = await Package.Fetch( packageIdent, false );
    if ( package == null || package.Revision == null )
        return null;

    await package.MountAsync();

    var primaryAsset = package.GetMeta( "PrimaryAsset", "" );
    return Model.Load( primaryAsset );
}

Referencing assets via component properties

Expose a typed property on a component and the editor will show a picker that accepts both local and cloud assets.
public Model MyModel { get; set; }      // local or cloud model
public Material MyMaterial { get; set; } // local or cloud material

Custom asset types

Define your own asset type by extending GameResource and decorating it with [AssetType].
[AssetType( Name = "Clothing Definition", Extension = "clothing", Category = "citizen" )]
public partial class Clothing : GameResource
{
    public string Title { get; set; }

    [ResourceType( "vmdl" )]
    public string Model { get; set; }
}
File extensions must be all lowercase and eight characters or fewer, otherwise the asset type will fail to register.
Once defined, the type appears in the New menu inside the Asset Browser. Load instances at runtime using ResourceLibrary:
// Load by path — returns null if not found
var clothing = ResourceLibrary.Get<Clothing>( "config/tshirt.clothing" );

// Load with a success check
if ( ResourceLibrary.TryGet<Clothing>( "config/tshirt.clothing", out var loaded ) )
{
    // use loaded
}
Use PostLoad to build a static catalogue of all instances:
public partial class Clothing : GameResource
{
    public static IReadOnlyList<Clothing> All => _all;
    internal static List<Clothing> _all = new();

    protected override void PostLoad()
    {
        base.PostLoad();
        if ( !_all.Contains( this ) )
            _all.Add( this );
    }
}

File system

Standard .NET file access (System.IO.File) is restricted in s&box. Use the provided BaseFileSystem instances instead.
Reads and writes to a subdirectory for the currently running game: C:\steam\steamapps\common\sbox\data\org\game\
if ( !FileSystem.Data.FileExists( "player.txt" ) )
    FileSystem.Data.WriteAllText( "player.txt", "Hello, world!" );

var text = FileSystem.Data.ReadAllText( "player.txt" );
An aggregate view of every mounted package: the core game, the current game, and its dependencies. Use this to read asset files shipped with packages.
Stores data shared across all games in your organization: C:\steam\steamapps\common\sbox\data\org\

Reading and writing JSON

WriteJson and ReadJson only serialize C# properties (not fields). Make sure the data you want to save uses { get; set; }.
public class PlayerData
{
    public int Level { get; set; }       // serialized
    public int MaxHealth { get; set; }   // serialized
    public string Username;              // NOT serialized (field)

    public static void Save( PlayerData data )
    {
        FileSystem.Data.WriteJson( "player.json", data );
    }

    public static PlayerData Load()
    {
        return FileSystem.Data.ReadJson<PlayerData>( "player.json" );
    }
}