Skip to content
SiteEmail

Image assets in Gameface should be selected based on the shape of the artwork and the amount of runtime control the UI needs.

Use SVGs for simple scalable graphics, such as icons, logos, dividers, and HUD frames. Use raster images , such as PNGs, for detailed painted artwork, portraits, screenshots, and textures where vector path evaluation would add unnecessary work.

In a browser prototype, it is common to pick an image format based mainly on visual quality and file size. In a gameface UI, that decision also affects how much work the UI layer performs during layout, rendering, and invalidation.

SVGs and raster images solve different problems:

  • SVGs describe shapes mathematically. They stay sharp at different sizes because the renderer draws paths, fills, and strokes at the requested scale.
  • Raster images store a fixed grid of pixels. They are better for detailed artwork because the expensive visual detail is already baked into the image file.

The important distinction is that SVG detail is evaluated by the UI renderer, while raster detail is decoded into pixels and then drawn as an image. A simple SVG icon is usually cheap and flexible. A complex SVG illustration with many paths, filters, gradients, or nested groups can become more expensive than a PNG that already contains the same final pixels.

Use SVGs for artwork that is mostly geometric : Icons, flat logos, simple button borders, arrows, separators, and HUD outlines usually benefit from SVG because they scale cleanly across different resolutions and aspect ratios.

Use raster images for artwork that is visually dense : Character portraits, item illustrations, painted backgrounds, screenshots, and textured panels usually belong in PNG, JPEG, or another raster format. These assets contain color variation and detail that would require many vector paths to reproduce.

InventoryCard.tsx
import Block from '@components/Layout/Block/Block';
import Image from '@components/Media/Image/Image';
const InventoryCard = () => {
return (
<Block class="inventory-card">
<Image class="item-portrait" src="/assets/items/dragon-armor.png" />
<Image class="rarity-icon" src="/assets/icons/legendary-star.svg" />
</Block>
);
};

For most Gameface UI work, the format decision can be made with a few direct rules:

  • Use SVG for icons, simple logos, flat badges, line art, corner decorations, borders, and scalable HUD shapes. And when you need to manipulate the asset at runtime.
  • Use PNG for transparent high-fidelity UI art, painted panels, portraits, item art, and alpha-heavy textures.
  • Use JPEG only when the asset does not need transparency and compression artifacts are acceptable, such as some large background images (although the use case is very rare).

When images are displayed at a significantly smaller size than their source resolution — for example a downscaled map icon or a UI element rendered in a 3D viewport — the GPU must sample the full-resolution texture down to fewer pixels. Without pre-generated mip-maps, this produces aliasing and sharp edges that look incorrect.

Gameface supports DDS textures with pre-baked mip-maps. If your project integrates Gameface through Unreal Engine, textures exported from Unreal already carry mip-maps natively. Use DDS for any raster asset that will be displayed at variable scale or as part of a 3D UI surface.

Standard PNG and JPEG assets do not carry mip-map data; the engine generates a single mip level at load time. For most HUD and menu assets that are displayed at a fixed size, this is fine.

Working with SVG Assets: External vs. Inline

Section titled “Working with SVG Assets: External vs. Inline”

When using SVGs in Gameface or modern web UIs, you should decide how to load them based on your use case. There are two primary strategies for SVG assets: external SVGs and inline SVGs. Understanding when to use each will help you balance performance, maintainability, and flexibility.

If your SVG does not need to be manipulated at runtime (for example, recolored or animated by the UI), keep it external. This approach minimizes DOM complexity and keeps your structure clean. There are two common external SVG patterns:

Use when the SVG is meaningful to the content or needs to participate in layout like an image. For example, as a button with an Xbox A glyph icon.

NavButton.tsx
import Button from '@components/Basic/Button/Button';
import Image from '@components/Media/Image/Image';
const NavButton = () => {
return (
<Button class="nav-button">
<Image class="nav-button__icon" src="/assets/icons/xbox-a.svg" />
<span class="nav-button__label">Select</span>
</Button>
);
};
external-svg-img.css
.nav-button {
display: flex;
align-items: center;
gap: 10px;
height: 56px;
padding: 0 18px;
}
.nav-button__icon {
width: 24px;
height: 24px;
}

Use when the SVG is purely decorative and part of the component’s styles. For example, a panel frame with a border.

QuestPanel.tsx
import BackgroundImage from '@components/Media/BackgroundImage/BackgroundImage';
import TextBlock from '@components/Basic/TextBlock/TextBlock';
const QuestPanel = () => {
return (
<BackgroundImage
src="/assets/ui/panel-frame.svg"
class="quest-panel"
style={{ width: '520px', padding: '32px' }}
>
<div class="quest-panel__title">Active Quest</div>
<TextBlock>Reach the old watchtower before nightfall.</TextBlock>
</BackgroundImage>
);
};
external-svg-background.css
.quest-panel {
width: 520px;
padding: 32px;
background-image: url("/assets/ui/panel-frame.svg");
background-repeat: no-repeat;
background-size: 100% 100%;
}

Use inline SVG (putting <svg> markup directly in your HTML) only if you need fine-grained control over the SVG’s internal structure at runtime. This is essential for:

  • Per-part styling (e.g., recoloring specific paths or elements)
  • State-based visuals (e.g., changing only one element within the SVG)
  • Adding JavaScript logic to specific parts of the SVG

Example: The canonical inline SVG example - a weapon icon with per-rarity path coloring using CSS custom properties and modifier classes - is covered in full detail in SVG UI Tricks: ViewBox, Strokes, and Fills.

The following table summarizes when to use each SVG loading method:

ApproachBest ForMain BenefitMain Cost
<img src="...svg">Static, meaningful iconsSmall DOM, simple layoutCan’t style SVG internals
CSS background-imageDecorative frames & separatorsKeeps decoration in CSSNot semantic, not accessible
Inline SVGDynamic or stateful vector elementsRich runtime controlAdds nodes & complexity to DOM

Gameface supports SVGs in the most common frontend scenarios:

  • Inline in the DOM.
  • As a CSS background-image.
  • As the source of an <img> element.
  • As a border through border-image-source.

The supported feature set is a subset of SVG 2.0. Standard CSS styling and CSS animations are supported for inline SVG elements.

FeatureNotes
Inline SVG in the DOMFull CSS styling and CSS/WAAPI animations
CSS background-imageCached when content size ≤ 1024×1024
<img src="...svg">Always cached
border-image-sourceSeparate surface per 9-slice section
@keyframes on SVG propertiesUnits required for shared CSS/SVG property names (e.g. width)
CSS transitionsSupported on all animatable SVG properties
<use> elementDeprecated in SVG 2.0 but still supported
CategoryDetails
SMIL animation<animate>, <animateTransform>, <set> - convert to CSS keyframes
Scripting & interactivityNo script execution inside SVG elements
<foreignObject>Embedded HTML inside SVG is ignored
pattern paint serverSVG <pattern> fills are not supported
vector-effectsNot supported
paint-order, color-interpolationNot supported
<a> and <image> linking elementsNot supported
MultimediaNot supported
Conditional processing, metadata, WAI-ARIANot supported
Full SVG textOnly basic <text> support; <textPath> ignored, <tspan> text treated as part of parent

These properties only work on <path> elements. They do not work on <circle>, <rect>, <line>, or other SVG shape primitives. If you need a circular or rectangular progress shape, draw it as a <path>.

Several properties share names between CSS and SVG but follow different rules. In SVG attributes, width, height, and font-size may be unitless. In CSS keyframes targeting SVG elements, these values must include units (px, em, etc.) even when the SVG attribute normally does not. SVG-only attributes like x, y, cx, and cy remain unitless.

Here is a practical example of markup that should be avoided in Gameface because it relies on unsupported SVG features:

unsupported-svg-example.svg
<svg viewBox="0 0 240 120" xmlns="http://www.w3.org/2000/svg">
<!-- Unsupported: SVG scripting/interactivity -->
<script>
console.log("SVG script execution");
</script>
<!-- Unsupported: foreignObject -->
<foreignObject x="10" y="10" width="220" height="100">
<div xmlns="http://www.w3.org/1999/xhtml">Embedded HTML in SVG</div>
</foreignObject>
<!-- Unsupported: SMIL animation -->
<circle cx="60" cy="60" r="24" fill="#4ecdc4">
<animate attributeName="r" from="10" to="28" dur="0.8s" repeatCount="indefinite" />
</circle>
</svg>

Before relying on a less common SVG element or attribute, check the official Gameface SVG support table . This is especially important when importing SVGs exported from design tools, because those files can include metadata, masks, clip paths, text nodes, grouped effects, or unsupported paint features that are not visible at first glance.

Instead of treating this as a single checklist, make the decision in three short passes: asset type, loading mode, then compatibility validation.

Start by asking what visual problem the asset solves:

  • If it is high-detail artwork (portraits, painted panels, item art), use a raster format.
  • If it is geometric UI structure (icons, borders, separators, symbols), use SVG.

Once the asset is SVG, decide how it enters the DOM:

  • Use external loading (<img> or CSS background-image) for static visuals.
  • Use inline SVG only when you must target internal nodes (for example per-path color states, stroke progression, or viewBox-driven effects).

Before integrating exported SVGs from design tools:

  • Open the SVG and remove editor metadata, hidden groups, and unused nodes.
  • Check for unsupported features (for example <foreignObject>, SMIL tags, scripting blocks).
  • Cross-check uncommon elements and attributes against the official support table.