Icon packs
Since Laby Client 47, every icon in the Laby Client interface is its own PNG or SVG file. The old sprite sheets are gone. Laby Client stitches the files into a texture atlas at runtime, and an icon pack is a folder of files that replaces some or all of them. Players pick an icon pack in the theme settings. Resource packs and addons can both ship one.
How an icon is found
Every icon has a sprite id made of a namespace and a path, for example labymod:common/gear. The path is the file path
below the source folder, without the extension. Laby Client checks these folders in order and takes the first file it
finds:
| Order | Folder | Contains |
|---|---|---|
| 1 | assets/<namespace>/icon_packs/<selected pack>/ |
The icon pack the player selected |
| 2 | assets/<namespace>/themes/<theme>/textures/gui/ |
The active theme, then each of its parents |
| 3 | assets/<namespace>/icon_packs/default/ |
The default icons, used by every theme |
Take labymod:common/gear. If the player selected a pack with the id outline, Laby Client reads
assets/labymod/icon_packs/outline/common/gear.svg. If not, and the fancy theme has its own gear, it reads
assets/labymod/themes/fancy/textures/gui/common/gear.png. Otherwise it falls back to
assets/labymod/icon_packs/default/common/gear.png.
Two more rules apply when one folder has more than one candidate:
- If a folder holds both
gear.pngandgear.svg, the SVG wins. - If several packs provide the same file, the pack with the higher priority wins. A resource pack the player enabled always beats an addon and Laby Client itself.
An icon pack doesn't have to cover every icon. Anything it leaves out comes from the theme, then from the default icons.
Hot reload
When you run your addon from the IDE, Laby Client rebuilds the atlas each time you save an icon file. You can edit icons with the game still running.
Icons for your addon
Put your icons into the default icon pack of your own namespace. If your namespace is example, the file
assets/example/icon_packs/default/common/heart.png becomes the sprite example:common/heart.
src/main/resources/assets/example/
└── icon_packs/
└── default/
├── common/
│ └── heart.png
└── mods/
└── features/
├── example.png
└── example_configuration/
└── enabled.png
Using an icon in code
Icon.lazyThemeGuiSprite creates an icon from a sprite id. It's safe to keep in a constant, because the icon looks up
its sprite every time it's drawn. Theme changes, icon pack changes and resource reloads don't break it. While no folder
has the sprite, it draws nothing. This is the method you want in almost every case.
public final class ExampleIcons {
public static final Icon HEART = Icon.lazyThemeGuiSprite(
ResourceLocation.create("example", "common/heart")
);
private ExampleIcons() {
}
}
Use Icon.themeGuiSprite when you need to know whether the sprite exists right now. It returns null if neither the
selected pack, the theme nor the default icons have it. Call it only after the game has finished loading, since Laby
Client builds the atlas after it enables addons.
Icon heart = Icon.themeGuiSprite(ResourceLocation.create("example", "common/heart"));
if (heart == null) {
heart = Icon.texture(ResourceLocation.create("example", "textures/heart_fallback.png"));
}
Icons for settings and HUD widgets
Settings and HUD widgets take their icons from mods/features/ with no code at all. The
file names are the class and field names in snake case.
| Element | Sprite path | Example |
|---|---|---|
| HUD widget | mods/features/<widget id> |
widget id example → mods/features/example.png |
| Setting in a config | mods/features/<config class>/<field name> |
field enabled in ExampleConfiguration → mods/features/example_configuration/enabled.png |
Laby Client strips a leading Default and a trailing Config from the config class name, then a trailing
HudWidget. So the setting showIcon in ExampleHudWidgetConfig looks for mods/features/example/show_icon.png.
Characters that resource paths don't allow turn into _.
If the file exists, it wins over an icon set with @SpriteSlot or setIcon. If it doesn't, Laby Client keeps using
the old icon, so existing addons don't lose theirs.
Icons for one theme only
To give a theme its own version of an icon, put a file with the same path into that theme's textures/gui folder.
assets/example/themes/fancy/textures/gui/common/heart.png replaces example:common/heart while the fancy theme is
active. A selected icon pack still wins over it.
Creating an icon pack
An icon pack is a folder below icon_packs/ with a pack.json in it. The folder name is the pack's id. The folder
can live in a resource pack or in an addon.
assets/labymod/icon_packs/outline/
├── pack.json
└── common/
├── gear.svg
└── back.svg
{
"name": "Outline",
"themes": ["fancy"]
}
| Field | Description |
|---|---|
name |
The name shown in the theme settings. Defaults to the id. |
themes |
The ids of the themes the pack is made for. Leave it out to offer the pack for every theme. |
You can't use the id default. Laby Client reserves it for the default icons, and players can't select it.
The Laby Client jar has every default icon under assets/labymod/icon_packs/default/. Copy the path of the icon you
want to replace into your pack. icon_packs/<your id>/common/gear.png replaces icon_packs/default/common/gear.png.
Covering several namespaces
One pack can replace Laby Client icons and addon icons at the same time. Use the same id in every namespace. Only one
of them needs a pack.json.
assets/
├── labymod/icon_packs/outline/
│ ├── pack.json
│ └── common/gear.svg
└── example/icon_packs/outline/
└── common/heart.svg
If several packs ship a pack.json for the same id, Laby Client takes the name and themes from the pack with the
highest priority. It still uses the icon files of all of them, ordered by priority.
Selecting a pack
Players choose the pack in the Icon Pack dropdown below the theme picker. The dropdown lists Default and every pack made for the current theme. Each theme remembers its own choice.
Laby Client looks for new packs after every resource reload. A resource pack with an icon pack shows up in the dropdown as soon as the player enables it.
File formats
PNG
Laby Client draws PNG icons with nearest filtering by default, which keeps pixel art sharp. For smooth, high resolution icons, switch to linear filtering with a sidecar file.
SVG
Laby Client rasterizes SVG icons at the current GUI scale and again whenever the scale changes, so they never blur.
The width and height of the <svg> element set the icon's size in GUI units. SVG icons always use linear
filtering. For new icons, SVG is the better choice.
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
</g>
</svg>
Laby Client supports static SVG with path, rect, circle, ellipse, line, polyline, polygon, groups, use
and symbol, transforms, linear and radial gradients, clip paths and <style> sheets. Text, embedded images, filters,
masks and animations don't work. A file can be up to 4 MB, and Laby Client rasterizes it at no more than 2048 pixels
per side.
Match the existing icons
Laby Client's own SVG icons are white line icons, 16 × 16 units, with a stroke and no fill. Draw yours the same way and they won't stand out next to the built-in ones.
Sidecar metadata
Any icon can have a .lmeta.json file next to it, named after the icon's full file name, for example
gear.png.lmeta.json or gear.svg.lmeta.json. Every field is optional.
{
"filter": "linear",
"padding": 2,
"scaling": {
"type": "nine_slice",
"width": 11,
"height": 11,
"border": { "left": 5, "top": 5, "right": 5, "bottom": 5 },
"stretch_inner": true
}
}
| Field | Description |
|---|---|
filter |
nearest (default) or linear. |
padding |
Gutter around the sprite in the atlas. Raise it if neighbouring sprites bleed into yours. |
scaling |
How the sprite stretches. One of stretch (default), tile or nine_slice. border takes one number for all sides or an object as above. |
animation |
Frames stacked in one image, with frame_width, frame_height, direction (vertical or horizontal), frametime in ticks, interpolate and an optional frames list. |
An icon pack icon without a sidecar of its own keeps the sidecar of the icon it replaces. If the default icon is a nine-slice, your replacement is a nine-slice too, until you give it its own sidecar.