Allow CreateSurfaceAppearanceAsync to Accept All Static Content Types
AssetService:CreateSurfaceAppearanceAsync is extremely useful for dynamically creating SurfaceAppearance objects, but its current content restrictions make several legitimate workflows unnecessarily difficult.
I would like to request that CreateSurfaceAppearanceAsync support all valid static content types that SurfaceAppearance properties themselves can accept, including direct image asset IDs.
Current documentation:
AssetService:CreateSurfaceAppearanceAsync documentation
The Problem
The current API makes developers go through additional steps when the content already exists as a static asset.
For example, a developer may already have:
- A ColorMap uploaded as an image asset
- A normal map uploaded as an image asset
- Multiple resolution variants of the same texture
- Pre-baked PBR textures
- Different texture sets for different skins
- Static textures bundled with a dynamically generated model
Yet instead of being able to directly construct the SurfaceAppearance from those existing assets, developers may need to create or manipulate intermediary content objects.
This becomes especially inconvenient when the goal is simply:
“Create a SurfaceAppearance using these already-existing static textures.”
There is no reason for the API to require developers to reconstruct static content that already exists.
Proposed API
Allow the content dictionary passed to CreateSurfaceAppearanceAsync to accept the same kinds of static content references that the corresponding SurfaceAppearance properties can use.
For example:
local surfaceAppearance = AssetService:CreateSurfaceAppearanceAsync({
ColorMap = Content.fromAssetId(COLOR_MAP_ID),
NormalMap = Content.fromAssetId(NORMAL_MAP_ID),
RoughnessMap = Content.fromAssetId(ROUGHNESS_MAP_ID),
MetalnessMap = Content.fromAssetId(METALNESS_MAP_ID),
})
The exact API representation can differ, but the important part is that existing static assets should be valid inputs without requiring unnecessary intermediate generation.
Why This Matters
1. Baked Content
Many workflows already produce baked textures externally.
A developer may have a complete PBR texture set generated in Blender or another DCC tool and uploaded to Roblox.
Forcing that content through another representation adds unnecessary work.
The developer already has the final data.
Let them use it.
2. Multiple Resolution Variants
Developers may intentionally maintain different texture resolutions depending on the model, platform, distance, or visual requirements.
For example:
Character
├── 1024×1024
├── 512×512
└── 256×256
A dynamic system could select the appropriate texture set when creating the appearance.
This becomes much cleaner if the API can directly reference the existing assets.
3. Dynamic Skins
This is even more important for games with large numbers of cosmetic variants.
Imagine a character system where skins are represented by different PBR texture sets:
local Skin = {
ColorMap = ...,
NormalMap = ...,
RoughnessMap = ...,
MetalnessMap = ...,
}
The game can then construct the appropriate SurfaceAppearance when needed.
Without direct static-content support, developers have to build additional machinery around what should fundamentally be a simple asset-selection operation.
4. Humanoidless Characters
This would also be particularly useful for developers building custom character systems without Humanoid.
A custom character pipeline may dynamically generate or assemble character geometry and then apply different static appearances to it.
This is a perfectly legitimate use case for SurfaceAppearance.
The more Roblox moves toward flexible custom character systems, the more important it becomes for appearance creation to be independent of the Humanoid ecosystem.
This Should Not Mean Allowing Arbitrary Runtime Data
I am not asking for unrestricted arbitrary network content or some mechanism that bypasses Roblox’s asset security.
The request is specifically about static Roblox-hosted content that is already valid and usable by the developer.
Roblox can still enforce:
- Asset ownership
- Permissions
- Moderation
- Content validation
- Supported image formats
- Resolution limits
- Any existing security requirements
The important distinction is:
“The asset is not allowed”
versus
“The asset is allowed, but this API refuses to accept it directly.”
The latter creates unnecessary friction.
The General Principle
CreateSurfaceAppearanceAsync should be a constructor for a SurfaceAppearance, not a requirement to rebuild its inputs.
If a developer already possesses valid static content:
Existing Asset
↓
CreateSurfaceAppearanceAsync
↓
SurfaceAppearance
should be a supported workflow.
There should not need to be an unnecessary chain of:
Existing Asset
↓
Intermediate Representation
↓
Additional Processing
↓
CreateSurfaceAppearanceAsync
↓
SurfaceAppearance
when the intermediate representation provides no additional value to the developer’s use case.
Proposed Change
Allow CreateSurfaceAppearanceAsync to accept all static content references supported by the underlying SurfaceAppearance properties, including image asset IDs / Content.fromAssetId(...).
This would make the API significantly more useful for:
- Dynamic skin systems
- Custom character systems
- Humanoidless characters
- Procedurally assembled models
- PBR material systems
- Baked texture pipelines
- Multiple-resolution asset pipelines
- Runtime model customization
- Games with large cosmetic inventories
The existing EditableImage workflow should remain supported.
The request is simply to remove the unnecessary restriction on static content that is already valid.
If Roblox already allows a SurfaceAppearance property to reference a static asset, developers should be able to use that same asset when constructing the SurfaceAppearance through CreateSurfaceAppearanceAsync.
Less intermediary machinery. More control.