Faces

Visual representations attached to templates, images, 3D models, and web views.

What are Faces?

Faces define how objects are visually presented to users. A single template can have multiple faces for different contexts, a thumbnail for list views, a full-resolution image for detail screens, a 3D model for AR experiences, or an interactive web view for rich applications.

Faces are the bridge between your tokenized data and the user experience. They turn abstract JSON properties into something people can see, touch, and interact with.

Face Types

Dual supports six face types, each optimized for a different rendering context:

  • Image, A static image (PNG, JPEG, SVG). The simplest face type. Use for product photos, certificates, and icons.
  • Image Progress, An image with a dynamic progress overlay. Use for gamification, level-up indicators, completion badges, or countdown visuals driven by object properties.
  • Image Policy, Policy-controlled image display. The face resolves to different images based on rules, for example, showing a "locked" image until a condition is met, then revealing the real content.
  • Image Layered, A composited multi-layer image. Each layer can be driven by object properties, enabling dynamic visual composition, e.g. a character avatar with interchangeable outfit layers.
  • 3D, A three-dimensional model (glTF/GLB). Use for AR product visualization, collectibles, or spatial experiences.
  • Web, An interactive HTML/JS web view. The most powerful face type, it can render anything a web page can, with access to the object's properties via a JavaScript bridge. Use for interactive dashboards, mini-games, or rich media experiences.

Registering a Face

Faces are registered against a template using POST /faces:

Code
POST /faces
{
"template": "io.acme.product.v1",
"display_type": "image",
"resources": ["https://cdn.example.com/product-hero.png"]
}

For web faces, the resources array points to the HTML file that renders the face. The web view receives the object's properties as a JSON payload, so it can render dynamic content:

Code
POST /faces
{
"template": "io.acme.ticket.v1",
"display_type": "web",
"resources": ["https://cdn.example.com/ticket-face/index.html"]
}

Multiple Faces per Template

A template can have many faces registered against it. Client applications select which face to render based on the display_type and the rendering context. For example:

  • A mobile app might request the image face for list views and the 3d face for the detail/AR view.
  • A web dashboard might use the web face for rich interactivity.
  • An email notification might fall back to the image face for broad compatibility.

List all faces for a template with GET /faces?template={templateId}, or retrieve faces by template using GET /faces/template/{templateId}.

Storage & Assets

Face assets (images, 3D models, HTML bundles) should be uploaded through the Dual Storage API and associated with the template:

Code
// Upload an asset
POST /storage/upload (multipart/form-data)
// Associate with template
POST /storage/template/{templateId} (multipart/form-data)

The Storage API returns a permanent URL for each uploaded file, which you then reference in the face's resources array. Using Dual's storage ensures assets are served from the same CDN infrastructure as the API.

Dynamic Faces

The most powerful face types, Image Layered, Image Progress, Image Policy, and Web, are dynamic. They read the object's current properties and render accordingly. This means:

  • A loyalty card face can show the current point balance without updating the face definition.
  • A ticket face can switch from "Valid" to "Redeemed" based on the status property.
  • A collectible face can reveal hidden layers as the owner completes achievements.

When an action updates an object's properties, the face automatically reflects the new state on the next render, no face re-registration needed.

Relationship to Other Concepts

  • Templates, Faces are registered against templates, not individual objects. All objects of a given template share the same face definitions.
  • Objects, When rendering a face, the client passes the object's properties to the face renderer. The face is the visual layer; the object is the data layer.
  • Storage, Face assets are hosted through the Storage API. Upload images, 3D models, and HTML bundles there.
  • Actions, Actions that update object properties implicitly change how dynamic faces render, without requiring any face-level changes.

Best Practices

  • Always register an image face, Even if your primary experience is a web face or 3D model, register a static image face as a fallback. Not all clients support rich face types.
  • Keep web faces lightweight, Web faces should load fast. Bundle your JS/CSS into a single HTML file and keep external dependencies minimal.
  • Use layered images for composition, If you need dynamic visuals but don't want the complexity of a web face, Image Layered gives you dynamic composition with the simplicity of static images.