Skip to content
SiteEmail

Two Gameface-specific rendering features unlock visual and performance capabilities that standard browser CSS cannot match. Backdrop filters blur or invert the live 3D scene behind a UI panel, not a static screenshot. UI Surface Partitioning renders each HUD component into its own texture, replacing a single full-resolution UI texture with several small ones. Both features involve cooperation between the frontend and the engine integration team.

The CSS backdrop-filter property lets an element apply filter effects (blur, invert, brightness, contrast) to whatever is rendered behind it. In a standard browser, “behind” means other DOM elements. In Gameface, “behind” can include the live 3D game scene, which is what makes this feature notable.

The CSS is standard:

HudPanel.tsx
import Block from '@components/Layout/Block/Block';
import TextBlock from '@components/Basic/TextBlock/TextBlock';
const HudPanel = () => (
<Block class="hud-panel">
<TextBlock class="hud-panel__title">Mission Briefing</TextBlock>
<TextBlock>Proceed to extraction point Alpha.</TextBlock>
</Block>
);
.hud-panel {
background-color: rgba(10, 10, 20, 0.4); /* semi-transparent so blur is visible */
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.15);
padding: 1.5rem 2rem;
border-radius: 6px;
}

When the engine integration provides the current frame’s rendered scene as the background texture, the blur samples from the actual 3D content directly behind the panel, not a flat color.


backdrop-filter does not blur everything behind an element. It blurs only the content within the current Backdrop Root Image, which is the section of the DOM that Gameface considers “the background” at the point where the filter is applied.

A new Backdrop Root Image starts whenever any ancestor element has one of these CSS properties active:

  • filter (any value other than none)
  • opacity less than 1.0
  • mask, mask-image, or clip-path (any value other than none)
  • backdrop-filter (any value other than none)
  • mix-blend-mode (any value other than normal)
  • coh-composition-id (any value)

Think of these as barriers. Content on the other side of a barrier is excluded from what the filter sees.

This example shows the practical consequence:

import Block from '@components/Layout/Block/Block';
const DimmedWrapper = () => (
<Block class="dimmed-wrapper">
<Block class="blurred-overlay" />
</Block>
);
.dimmed-wrapper {
opacity: 0.8; /* creates a new Backdrop Root Image here */
}
.panel-overlay {
backdrop-filter: blur(6px);
background-color: rgba(0, 0, 0, 0.3);
}

The opacity: 0.8 on .dimmed-wrapper acts as the barrier. The blur on .panel-overlay sees only the two card siblings, not the body background or the 3D scene. If you remove the opacity from the wrapper, the blur reaches back to the root element (the <body>), which includes the 3D scene if the engine has provided it.


For backdrop-filter to reach the actual 3D scene (not just other DOM elements), the engine integration must supply the current frame’s rendered scene as a “user-defined background” texture to Gameface before rendering begins. This texture is passed through a call on the Gameface View object each frame.

The engine team on your project handles this wiring. From the frontend side, the only requirements are:

  1. The element using backdrop-filter must have the root <body> element as part of its Backdrop Root Image, which means no opacity, filter, or mask properties on any ancestor between the <body> and the filtered element.
  2. coh-composition-id on any ancestor also creates a boundary that excludes the scene.

If the engine team has not yet provided the user background texture, backdrop-filter will still apply to whatever DOM elements are behind the filtered element. The 3D content simply will not be included.


backdrop-filter supports the same functions as filter:

.panel-frosted {
backdrop-filter: blur(10px);
}
.panel-dark-glass {
backdrop-filter: blur(6px) brightness(0.7);
}
.panel-inverted {
backdrop-filter: invert(1);
}
.panel-saturated {
backdrop-filter: saturate(2) blur(4px);
}

Multiple functions can be chained. They execute left-to-right on the same backdrop image.


Elements with backdrop-filter are redrawn every frame, regardless of whether the element itself has changed. This is by design: the scene behind the UI is assumed to change every frame, so Gameface cannot cache the filtered result.

This means the cost of each backdrop-filter element scales with the number of active backdrop filters in the DOM, not just whether content changed. A single blurred panel behind a HUD has an acceptable cost. Ten blurred panels on the same frame each re-sample and filter the backdrop separately.

Practical rules:

  • Limit backdrop-filter to elements that are genuinely large or contextually important. Icon overlays, tooltips, and minor decorative elements are not worth the per-frame cost.
  • Avoid animating backdrop-filter values (changing the blur radius in a loop). The value change forces the filter to re-execute each frame at full cost regardless of caching.
  • Where the frosted glass look is needed for performance-sensitive screens, consider faking it with a semi-transparent background and a static inner shadow instead of a live blur.

Standard Gameface rendering writes the entire UI to a single full-resolution texture equal in size to the view. At 4K (3840 × 2160), that texture is always fully allocated even when the actual UI content only occupies the corners of the screen.

UI Surface Partitioning resolves this by rendering each top-level UI component into its own individual texture. A minimap in the bottom-left corner gets its own small texture. The health bar in the top-left gets another. The engine composites these textures at their correct screen positions using its own rendering pipeline, and no single full-resolution UI texture is ever created.

The memory saving is significant: four 400×300 corner panels replace one 3840×2160 texture. The math is roughly 30x less texture memory for that UI.


Surface partitioning is the right tool when:

  • The UI is a HUD with discrete, spatially separated components (minimap, health bar, inventory slots, objective tracker).
  • Most of the screen area contains no UI elements.
  • The game runs at 4K or other high resolutions where the full-resolution UI texture cost is measurable.

It is the wrong tool when:

  • The UI covers most of the screen (full-screen menus, inventory screens, character creation).
  • UI elements overlap each other in complex ways.
  • The UI uses backdrop-filter extensively (partitioned elements create Backdrop Root Image boundaries that exclude scene content from filters).

The entire frontend change is adding two CSS properties to your top-level HUD elements.

coh-partitioned: on tells Gameface to render this element into its own texture. coh-composition-id assigns a string identifier the engine uses to map the rendered texture back to its screen position and visibility state.

HudRoot.tsx
import Block from '@components/Layout/Block/Block';
const HudRoot = () => (
<>
<Block
class="hud-minimap"
attr:coh-partitioned="on"
attr:coh-composition-id="minimap-surface"
/>
<Block
class="hud-health-bar"
attr:coh-partitioned="on"
attr:coh-composition-id="health-surface"
/>
</>
);

In practice, apply these properties via a stylesheet class rather than inline styles to keep the HTML clean:

hud-partitioning.css
.hud-minimap {
position: fixed;
bottom: 2rem;
left: 2rem;
width: 280px;
height: 280px;
coh-partitioned: on;
coh-composition-id: minimap;
}
.hud-healthbar {
position: fixed;
bottom: 2rem;
right: 2rem;
width: 320px;
height: 80px;
coh-partitioned: on;
coh-composition-id: healthbar;
}
.hud-objectives {
position: fixed;
top: 2rem;
right: 2rem;
width: 300px;
height: auto;
coh-partitioned: on;
coh-composition-id: objectives;
}
.hud-ammo {
position: fixed;
bottom: 6rem;
right: 2rem;
width: 200px;
height: 60px;
coh-partitioned: on;
coh-composition-id: ammo;
}

Top-level only. coh-partitioned: on only works on direct children of <body>. Applying it to a nested element (a child of a child) will result in the element not rendering and a warning in the console. Every partitioned element must be a first-level descendant of <body>.

All or nothing. When the engine initializes the view with surface partitioning enabled, every top-level DOM element must be marked with coh-partitioned: on. If any top-level element is left unmarked, its rendering will be incorrect. The two supported configurations are: partitioning enabled with all top-level elements marked, or partitioning disabled with no elements marked. Mixing is not supported.

No draw-order management. Gameface does not inform the engine about the z-index of partitioned elements. If two partitioned panels need to render in a specific depth order (one visually above the other), the engine integration team must handle that ordering explicitly in the compositor. Do not rely on CSS z-index between partitioned siblings to produce correct overlap in the final composited image.

coh-composition-id creates a Backdrop Root Image boundary. Partitioned elements cannot use backdrop-filter to blur the 3D scene through their partition boundary. The scene-blur use case and surface partitioning are mutually exclusive at the element level.