◐
material · drop-in viewer
asset⌬ materialmaterialprimary: mat.yaml·part ofpackage glassLiquidsMaterials.package·originates fromworld 5ea0a517-c…
What it does
No description provided yet.
Interface
What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.
This asset doesn't declare a schema or structured payload.
⌬ Metadata
description
Sub-parts
Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.
Showing up to 3 levels deep. Open a part to see what it holds further down.
10+items
# clearMedium
A see-through dielectric that shows the lit room behind it: glass, water, ice, crystal, gems, honey and slime in `glassLiquidsMaterials`. The engine's `pbr` transmission refracts only the sky (gh#10617), so this shader reads the room through the engine's scene-colour copy (`zero::scene_color`) at the place the refracted ray really lands, measured with the scene depth (`zero::scene_depth`): a thin pane barely moves the room, hammered or cracked glass bends it by (ior − 1) × tilt over the distance behind, a ball lens inverts it.
Colour comes only from the medium: Beer–Lambert absorption `T = absorb_color ^ (path / absorb_distance)` over the real path (a pond as deep as its floor, a sphere as thick as its chord, a slab `thickness / cos θ`), single scattering for cloudy media (`scatter_color`, `scatter_distance`, `scatter_g`), and the engine's own PBR lighting (`zero_pbr_shade`: every light, shadows, the environment) for the reflection, with the surface tracing the room's reflection for itself (`screenReflection`). Also: per-colour refraction (`dispersion`, glTF's 20 / Abbe number), frost blur that grows with the distance behind, painted parts (lead, wire) and inclusions (crack faces, bubbles at two depths, veils) from a mask, gem facets and the light a stone sends back out after reflecting inside it (`sparkle`), and gravity–capillary ripples periodic over one UV repeat that a `flow` carries downstream.
`cover = 0` makes a clear shell that composites over what is drawn behind it (other see-through things too); `cover = 1` replaces what is behind with its own bent, absorbed read. Draw it premultiplied: shells at queue 3010 without depth, bodies at 3000 writing depth (see the package README, "How they draw").
▲ 0↑ born
# glassLiquidsMaterials: Looking Glass
**25 see-through materials for the Swatchbook material library, each reached by one name: clear, frosted, green tinted, smoky tinted, stained, cracked and wired glass; still, flowing and murky water; clear and cloudy ice; rock crystal and six gems; honey, slime and candle wax; and toon water, glass and gem. They show the room behind them, bend it by their real index of refraction and take their colour from what they are made of.**
**Version 1.0.5. Depends on materialLibraryKit 1.3.1** (Swatchbook), which installing this package brings with it. Nothing else is installed (no copies of the engine's own parts: the package names them with no engine version, so it uses whatever engine your world runs) and there is no setup code: once installed, every material is in the library's list (category `translucent`), picker and swatch gallery on every player's machine.
## Try it
Install **glassLiquidsMaterials** from ZeroMind (search "glassLiquidsMaterials" or "Looking Glass"). It lands at `/source/glassLiquidsMaterials.package`, with materialLibraryKit 1.3.1 in a folder under `/source/deps/` (scripts reach it by name, wherever it sits). Then paste this into the console (`execute`) or any script, as it is, in an empty world or any other:
```luau
local Materials = require("~materialLibraryKit.Materials")
local cube = entity.spawn("lookingGlassCube", { synced = true, components = { Model = { model = "cube" } } })
cube.localPosition = { 0, 1, 3 }
assert(Materials.apply(cube, "glassTintedGreen")) -- every player sees a green glass cube
for _, m in Materials.list({ category = "translucent" }) do
print(m.id, m.kind, m.style, m.description) -- each description opens with its IOR and absorption
end
Materials.clear(cube) -- take the glass off again, for everyone
entity.despawn(cube.id) -- and the cube
```
`Materials.apply(object, name)` puts a material on any `Model` and shares the choice with every player; `Materials.get(name).material` is the material asset itself, for a `Model` of your own. `Materials.list({ category = "translucent", ... })` lists the whole collection and filters by `kind` (`glass`, `water`, `ice`, `quartz`, `gem`, `honey`, `slime`, `wax`), `style` (`realistic`, `toon`), `animated`, `tag` (`refraction`, `absorption`, `scattering`, `dispersion`, `frost blur`, `sparkle`, `ripples`, `flowing`, `shell`, `body`, `subsurface`, `textures-none`, `ior-1.52`, `absorb-6mm`…) or `search`. Every row's description opens with its optics, for example "IOR 1.52; absorbs to (0.30, 0.62, 0.20) over 6 mm.", and collection.yaml carries them as `ior`, `absorbColor`, `absorbDistance` and `absorbModel`. A cartoon world swaps by changing only the name: `waterStill` → `toonWater`, `glassClear` → `toonGlass`, `gemRuby` → `toonGem`.
To walk round every material, make your start scene the gallery (as the Looking Glass world's own `scenes/main.scene` does), or stand it in a scene of your own (`build.luau`, spawn near the origin):
```luau
local g = entity.spawn("Gallery")
g.component.add("SwatchGallery", { view = "all", lighting = "studio" })
g.component.add("GlassShowcase", {}) -- all 25 on one ring, each beside its reference picture
require("~glassLiquidsMaterials.GlassExhibits").build() -- windows, a glass of water, a pond with ice and gems, a stream
```
## The materials
Every material is tagged `category = translucent` with its `kind`, `style`, `tileSize` (metres one repeat covers; `1` and `tile-uniform` for those with no repeating pattern), roughness and metallic ranges, `textureKB` (GPU memory, mipmaps included) and `animated`. Its tags add the see-through features it relies on (next section), a finish, a metal word, a texture weight, `tile-<metres>m`, `animated` / `not-animated`, and search words.
Textures lists albedo / normal / mask sizes in pixels (`-` means none). Seam is `Checks.seams`' score at 4 × 4: the contrast across the tile edges divided by the material's average contrast inside a tile, so 0 means the edge is invisible. The check passes a material when that ratio is at most 2, or when the edge is no stronger than the material's own strongest lines inside a tile (lead came, facets, cel bands), which is why `glassStained` (2.30) passes: its lead lines cross the tile edges as they cross everywhere else. Every other material scores 0. All 25 pass. Shimmer is `Checks.shimmer`'s score at 30 m (fails above 6). Measured on 2026-10-26 (1.0.2) with materialLibraryKit 1.3.1 on the desktop engine. IOR is the index of refraction; "Absorbs to (over)" is the colour white light turns after that distance inside the material (Beer-Lambert), so twice the distance gives that colour squared.
| Name | Material | Kind, style | Tile (m) | Real sizes | IOR | Absorbs to (over) | Roughness | Metal | Textures (memory) | Seam / shimmer |
|---|---|---|---|---|---|---|---|---|---|---|
| **Glass** | | | | | | | | | | |
| `glassClear` | Clear window glass | glass | uniform | 6 mm pane | 1.52 | (0.72, 0.86, 0.80) over 1 m | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `glassFrosted` | Frosted glass | glass | 0.02 | etch pits 10 to 50 µm; 20 mm repeat | 1.52 | (0.72, 0.86, 0.80) over 1 m | .42 | .00 | - / 256 / - (85 KB) | 0.00 / 0.00 |
| `glassTintedGreen` | Green tinted glass | glass | uniform | two 3 mm walls | 1.52 | (0.30, 0.62, 0.20) over 6 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `glassTintedSmoke` | Smoky tinted glass | glass | uniform | 6 mm pane | 1.52 | (0.58, 0.50, 0.42) over 6 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `glassStained` | Stained cathedral glass | glass | 0.5 | pieces 60 to 150 mm, lead came 6 mm wide; 500 mm repeat | 1.52 | each piece its own colour, over 3 mm | .04-.70 | .00 | 512 / 512 / 512 (1.0 MB) | 2.30 / 3.64 |
| `glassCracked` | Cracked glass | glass | 0.6 | shards 30 to 150 mm, cracks under 1 mm wide; 600 mm repeat | 1.52 | (0.72, 0.86, 0.80) over 1 m | .00-.30 | .00 | - / 512 / 512 (683 KB) | 0.00 / 1.55 |
| `glassWired` | Wired safety glass | glass | 0.0508 | 0.5 mm wire on a 12.7 mm square grid, half a square off the repeat edges; 4 x 4 squares per 50.8 mm repeat | 1.52 | (0.72, 0.86, 0.80) over 1 m | .00-.40 | .00 | - / - / 256 (85 KB) | 0.00 / 0.00 |
| **Water** | | | | | | | | | | |
| `waterStill` | Still clear water | water | 1 | capillary ripples 30 to 170 mm; 1 m repeat | 1.333 | (0.71, 0.94, 0.98) over 1 m | .01 | .00 | - / - / - (none) | 0.00 / 0.01 |
| `waterFlowing` | Flowing stream water | water | 1 | ripples 70 to 330 mm moving at 0.6 m/s; 1 m repeat | 1.333 | (0.62, 0.88, 0.86) over 1 m | .02 | .00 | - / - / - (none) | 0.00 / 0.58 |
| `waterMurky` | Murky pond water | water | 1 | faint ripples 40 to 200 mm; 1 m repeat | 1.333 | (0.50, 0.42, 0.16) over 1 m | .03 | .00 | - / - / - (none) | 0.00 / 0.01 |
| **Ice** | | | | | | | | | | |
| `iceClear` | Clear ice | ice | 0.25 | melt undulation 20 to 120 mm; 250 mm repeat | 1.31 | (0.68, 0.92, 0.98) over 1 m | .02 | .00 | - / 256 / - (85 KB) | 0.00 / 0.54 |
| `iceCloudy` | Cloudy ice | ice | 0.2 | bubbles 0.5 to 3 mm in layers up to 30 mm deep; 200 mm repeat | 1.31 | (0.70, 0.92, 0.98) over 1 m | .14 | .00 | - / 256 / 256 (171 KB) | 0.00 / 0.07 |
| **Crystal and gems** | | | | | | | | | | |
| `quartzCrystal` | Rock crystal quartz | quartz | 0.3 | veils 10 to 60 mm inside; colour depth set for a 60 mm crystal; 300 mm repeat | 1.544 | (0.97, 0.97, 0.97) over 0.1 m | .00 | .00 | - / - / 256 (85 KB) | 0.00 / 1.25 |
| `gemRuby` | Ruby | gem | 1 | colour depth set for a cut stone 8 mm deep, saturated at 5 mm | 1.77 | (0.85, 0.05, 0.11) over 5 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `gemSapphire` | Sapphire | gem | 1 | colour depth set for a cut stone 8 mm deep, saturated at 5 mm | 1.77 | (0.07, 0.20, 0.85) over 5 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `gemEmerald` | Emerald | gem | 1 | colour depth set for a cut stone 8 mm deep, saturated at 5 mm | 1.58 | (0.12, 0.70, 0.36) over 5 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `gemAmethyst` | Amethyst | gem | 1 | colour depth set for a cut stone 8 mm deep, saturated at 5 mm | 1.544 | (0.60, 0.26, 0.85) over 5 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `gemTopaz` | Golden topaz | gem | 1 | colour depth set for a cut stone 8 mm deep, saturated at 5 mm | 1.62 | (0.95, 0.60, 0.16) over 5 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `gemDiamond` | Diamond | gem | 1 | colour depth set for a cut stone 8 mm deep, saturated at 5 mm | 2.417 | (0.99, 0.99, 0.98) over 10 mm | .00 | .00 | - / - / - (none) | 0.00 / 0.00 |
| **Honey, slime and wax** | | | | | | | | | | |
| `honey` | Honey | honey | uniform | colour depth: pale gold at 5 mm, amber at 50 mm | 1.49 | (0.88, 0.55, 0.12) over 30 mm | .02 | .00 | - / - / - (none) | 0.00 / 0.00 |
| `slime` | Green slime | slime | 0.1 | bubbles 1 to 4 mm; surface undulation 30 to 50 mm; 100 mm repeat | 1.35 | (0.40, 0.85, 0.15) over 50 mm | .05 | .00 | - / - / 256 (85 KB) | 0.00 / 0.05 |
| `candleWax` | Candle wax | wax | uniform | uniform (no texture) | 1.44 | opaque; scatters toward (0.85, 0.55, 0.30) | .42 | .00 | - / - / - (none) | 0.00 / 0.00 |
| **Toon** | | | | | | | | | | |
| `toonWater` | Toon water | water, toon | 1 | ripple strokes about 200 mm; 1 m repeat | 1.333 | toon tint (0.25, 0.85, 0.85), deeper beyond 0.5 m | .00 | .00 | - / - / - (none) | 0.00 / 0.63 |
| `toonGlass` | Toon glass | glass, toon | uniform | uniform (no texture) | 1.52 | toon tint (0.75, 0.92, 1.00), flat | .00 | .00 | - / - / - (none) | 0.00 / 2.43 |
| `toonGem` | Toon gem | gem, toon | 1 | facet cells 8 per repeat | 1.6 | toon tint (0.95, 0.25, 0.75), flat | .00 | .00 | - / - / - (none) | 0.00 / 0.74 |
Animated (it moves by itself, tagged `animated`): `waterFlowing` alone, running downstream at 0.6 m/s. Every other material is still (`not-animated`): still and murky water, slime and the toons hold their surface pattern without moving. Nothing in the collection is metallic: the lead came and the wire are dark non-metallic parts (metallic 0), like everything else.
## How they draw: the engine's see-through features
The engine's standard material can be made transparent, but its `transmission` refracts only the sky, never the room (gh#10617), and the built-in glass is a plain 30% alpha that reads as grey plastic (gh#19966). So the realistic materials draw with this package's **`clearMedium`** shader and the toons with **`toonClear`**, both built only on what the engine provides:
- **Scene colour** (`zero::scene_color`): the lit room as it stood before the see-through pass. The material reads it where its refracted ray lands, so it shows what is behind it, bent.
- **Scene depth** (`zero::scene_depth`): how far the room is behind each pixel. It sets how far the image moves (a thin pane barely shifts it, hammered or cracked glass bends it by (ior − 1) × tilt over that distance, a sphere inverts it like a lens) and how much colour deep things pick up (a pond is as deep as its floor).
- **Absorption** (Beer-Lambert, the same model as the engine's oceans): `T = absorb_color ^ (path / absorb_distance)` over the real path, so thicker or deeper stuff carries more colour. Pure water loses red first (0.71 / 0.94 / 0.985 per metre); gems saturate over a few millimetres of stone.
- **Scattering**: single scattering with a Henyey-Greenstein phase, so murky water, cloudy ice, slime and honey glow where light comes through them from behind.
- **Engine PBR lighting** (`zero_pbr_shade`): the reflection is the engine's own lighting (every light, shadows, the environment), weighed by Fresnel for the material's index of refraction, so glass is invisible face-on and a mirror at grazing angles.
- **Screen reflection** (`screenReflection`): each smooth surface traces the room's reflection for itself, which an engine reflection pass cannot do for a transparent surface.
- **Dispersion**: red and blue refract by different indices (glTF's 20 / Abbe number), giving fire at the edges of thick glass and gems.
- **Frost blur**: rough glass spreads the transmitted light by (ior − 1) × roughness², so it blurs things in proportion to their distance behind it. A hand pressed on frosted glass stays sharp.
- **Facets and sparkle** (crystal and gems): flat facets, and the light the stone sends back out after reflecting inside it, which is where a gem's brilliance and fire come from. Gems never glow: no material in the pack is emissive.
- **Ripples**: gravity-capillary waves at their physical speed, plus travelling noise ripples, periodic over one repeat so they tile, carried downstream by `flow`.
- **Subsurface** (`candleWax`): the engine's standard material with its subsurface lobe, in the opaque queue.
### Shells and bodies (draw order)
The engine draws see-through surfaces in a transparent queue after everything opaque, every one of them reading the same picture of the room (gh#18316 tracks that the order is undocumented; this is what was measured). The pack works with that rule instead of against it:
- **Shells** (`glassClear`, `glassWired`, `toonGlass`): a thin clear pane composites *over whatever is already drawn behind it*, other see-through things included, and adds its reflection, wire and scatter. Queue 3010, after every body, no depth write (gh#10111: the material says so explicitly), both faces. A glass of water, a window in front of a pond, or a jar of honey therefore never hides what is inside or behind it.
- **Bodies** (everything else that bends, blurs or tints): the medium replaces what is behind it with its own bent, absorbed read of the room. Queue 3000, **writes depth**, front faces. Writing depth means nothing behind a body can ever draw over it in the wrong order and no shell behind it can cut a hole in it. The cost is the engine's rule: a body shows the opaque room through it, not another see-through object behind it (a ruby behind a sapphire shows the room, not the ruby).
### Each material's features, and the web browser
| Name | Layer | Engine see-through features it relies on | In the web browser |
|---|---|---|---|
| `glassClear` | shell | pbr lighting, screen reflection | Same look. Cheapest: one reflection trace. |
| `glassFrosted` | body | scene colour, scene depth, refraction, pbr lighting, absorption, scattering, dispersion, frost blur | Same look. The heaviest read (14 extra room samples for the blur); no reflection trace. |
| `glassTintedGreen` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, dispersion | Same look. Medium. |
| `glassTintedSmoke` | body | as glassTintedGreen | Same look. Medium. |
| `glassStained` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, scattering, dispersion, lead came | Same look. Medium plus a second lighting pass on the lead; three 512 px textures. |
| `glassCracked` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, dispersion, inclusions | Same look. Medium. |
| `glassWired` | shell | pbr lighting, screen reflection, wire | Same look. Light: reflection plus a second lighting pass on the wire. |
| `waterStill` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, scattering, dispersion, ripples | Same look, still. Medium (ripples evaluated per pixel). |
| `waterFlowing` | body | as waterStill, plus flowing, foam | Same look, moving (the only animated material). Medium. |
| `waterMurky` | body | as waterStill | Same look, still. Medium. |
| `iceClear` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, dispersion | Same look. Medium. |
| `iceCloudy` | body | as iceClear, plus scattering, bubbles | Same look. Medium. |
| `quartzCrystal` | body | as iceClear, plus bubbles (veils), facets, sparkle | Same look. Heavier: internal light per colour and per light. |
| `gemRuby` … `gemDiamond` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, dispersion, facets, sparkle | Same look. Heavier: internal light per colour and per light, best kept to small objects. |
| `honey` | body | scene colour, scene depth, refraction, pbr lighting, screen reflection, absorption, scattering, dispersion | Same look. Medium. |
| `slime` | body | as honey, plus bubbles, ripples | Same look, still. Medium. |
| `candleWax` | opaque | subsurface (engine pbr) | Same look. Cheapest: the standard material. |
| `toonWater` | body | cel-shaded, scene colour, scene depth, refraction, foam lines | Same look, still. Light. |
| `toonGlass` | shell | cel-shaded, shine streaks | Same look. Light. |
| `toonGem` | body | cel-shaded, scene colour, refraction, facets | Same look, still. Light. |
**Measured in a web browser** (1.0.1, 2026-10-26: the world played in a browser tab of the Zero editor, WebGPU, viewport 840 × 632, measured from the desktop editor through the library's room reports). Both shaders compile in the browser, and no Looking Glass surface draws with anything but its own shader (each player's report carries this as `lookingGlass`: `require("~materialLibraryKit.Sync").heard()[peer].report.lookingGlass`). The pack uses nothing desktop-only: no 32-bit float filtering, no storage buffers (`Checks.budget`: 0 for every material), at most 3 sampled textures per material (5 for wax; the web limit is 9). Frame rate in that tab, the gallery switched through every step the library's tour takes:
| Browser view | fps | frame |
|---|---|---|
| Empty floor (no swatches: the engine's own ceiling, gh#20750) | 29.1 | 34 ms |
| Calibration page | 25.5 | 39 ms |
| Looking Glass page, all 25 bays | 28.7 | 35 ms |
| Close-up `glassClear` | 25.4-26.2 | 38-39 ms |
| Close-up `glassFrosted` | 26.3 | 38 ms |
| Close-up `glassStained` | 25.7 | 39 ms |
| Close-up `gemRuby` | 26.9-27.1 | 37 ms |
| Close-up `waterFlowing` | 25.3 | 40 ms (1.0.0: 19.7 fps, 51 ms) |
| Compare `gemRuby` / `glassStained` | 25.9 | 39 ms (1.0.0: 20.8 fps, 48 ms) |
So in the browser, a full screen of the heaviest material costs about 5 ms over the bare floor, and the ring of all 25 costs almost nothing. The light / medium / heavy words above rank what each material asks of the GPU per pixel; on a weaker GPU, put the heavy ones on small objects. Shimmer was checked at the browser's own sizes as well (`Checks.shimmer` at 840 × 632 and at half that, 420 × 316, for a browser rendering at half scale): all 25 stay under the limit of 6, the worst being `glassStained` 4.30 and `toonGlass` 2.75. To measure another machine, play the world on it and read its report from the editor (`Sync.heard()`). The browser numbers above were taken that way: a tour request (`require("~materialLibraryKit.Gallery").requestTour()`) sent from the desktop editor got no answer from the browser editor in play, so the shared gallery was switched through the tour's steps by hand and the tab's own report read after each.
## Texture memory
- **All 25 materials: 2.2 MB** of GPU texture memory (2,300 KB, mipmaps included, every texture counted once): 12 textures, all UASTC (BC7 on desktop, ASTC on phones), none over 512 px.
- **15 materials need no texture at all**: clear, green and smoky tinted glass, the three waters (their ripples are computed), the six gems, honey, wax and the three toons. The largest is `glassStained` at 1.0 MB.
- **Reference pictures: 8.3 MB** of GPU memory (25 at 512 px). They load only where the gallery hangs them.
- **Everything together: 10.5 MB**, about a thirty-sixth of the library's 384 MiB budget for low-end devices.
- **Within the web limits:** every material passes `Checks.budget` with no warnings.
- **Download:** the package is **610 files, 20.2 MB**: 14.1 MB reference pictures (JPEG sources plus their 512 px GPU data), 1.5 MB the 83 display-piece meshes, 1.5 MB the material textures, 2.8 MB the materials and reference boards with their previews. materialLibraryKit adds 139 files and 0.9 MB. Nothing else comes with it.
## How they were made
- **Reference pictures first.** Every material has `references/<id>`, generated from the prompt in its catalog entry before the material was made. Glass, ice and crystal are studio shots in front of a checkerboard card so the bending can be compared; panes are photographed straight on against daylight. Gems are macro jewellery shots, water and honey are scenes, and the toons are cel-shaded renders. Each material was compared with its picture side by side in the gallery under Studio light, and in use on the exhibits. The comparison changed several values: honey scattered far too much (real honey is clear amber), slime was opaque, gem colours were too pale, the crystal threw a bloomed glint, and the water caps were too deep.
- **Physical values.** Indices of refraction of the real materials (soda-lime glass 1.52, water 1.333, ice 1.31, quartz 1.544, corundum 1.77, beryl 1.58, topaz 1.62, diamond 2.417, honey 1.49); dispersion from Abbe numbers; absorption from measured spectra (pure water, iron-green float glass, bronze tint at about 50% transmission through 6 mm, gems saturated over a few millimetres). No colour carries painted-in light: absorption and scatter colours stay inside 0..1, nothing is emissive.
- **Procedural textures in Luau** (`GlassRecipes` through `GlassBaker`): stained cathedral glass (power-Voronoi pieces in seven body colours with density streaks, rounded 6 mm lead came, hammered surface), crazed glass (shards each knocked up to 1.5° off true, bright crack faces and hairlines), Georgian wire (0.5 mm on 12.7 mm), ice melt and bubbles, quartz veils, slime bubbles and the frosted etch. Every field is periodic over one repeat, so every map tiles by construction. Heights are in metres, written green-up (the engine's convention); the test suite proves the direction on a synthetic bump. `require("~glassLiquidsMaterials.GlassRecipes").bakeAll()` re-bakes all eight in about ten seconds.
- **Real size on the display pieces.** `GlassShapes` bakes a pane, pond and stream surfaces, a tumbler and its water, a bottle, a honey jar and its honey and a candle for every tile size, with UVs in metres ÷ tileSize. `GlassSurface` swaps a piece to the right one whenever any player applies another material. The display gems are 60 mm round brilliants (large museum stones) and the quartz point 140 mm.
### What was reused, and what was not
- **Used:** the library's collection contract, gallery, picker, lighting looks and checks (materialLibraryKit 1.3.1); syntheticsMaterials' baker, catalog, showcase, surface component and mesh pattern (Polish and Polymer, MIT, after Warp and Weft and the other collections); its `toonFinish` cel banding; the engine's scene-colour and scene-depth copies, `screenReflection` trace, PBR lighting and standard material with its subsurface lobe; the oceans package's Beer-Lambert colour model; the approach of the published glass and water shaders in Mother Liquor (`flask_glass`) and The Emperor's Noon (`water_surface`): reading the room along the refracted ray, and measuring the water column from the depth.
- **Not used, and why:** the engine's `glass` material (30% alpha, no refraction: gh#19966), `water` (opaque scrolling normals, no depth or transparency) and the oceans system (a camera-following sea with its own clipmap mesh, not a material for a glass, a pond or a stream). The published glass, water, ice and gem materials found on ZeroMind are hand-lit with the sun and point lights only (no environment or shadows), or are plain alpha blends with emissive glow standing in for colour. None of them tiles, carries a stated scale or matches the library's tags.
## Checked
On 2026-10-25 (1.0.0) and again on 2026-10-26 (1.0.1 and 1.0.2), desktop engine and a web browser, materialLibraryKit 1.3.1, for **all 25** materials:
- **Seamless tiled 4 × 4:** `Checks.seams` renders a 4 × 4-repeat plane and checks every texture's opposite edges. All 25 pass.
- **No shimmer or moiré at 30 m:** `Checks.shimmer`, all 25 under the limit of 6, at the check's own size and at the browser's 840 × 632 and 420 × 316. The worst are `glassStained` 3.64 (4.30 at 840 × 632), `toonGlass` 2.43 (2.75 at 420 × 316) and `glassCracked` 1.55. Ripples and facets fade out where a pixel holds many of them, so moving water does not sparkle into noise far away.
- **Web budget:** `Checks.budget` passes with no warnings for every material.
- **Against the references:** side by side in every gallery bay, and in use. The clear-glass sphere inverts the checkerboard like its photograph. The glass of water shows its water through the glass with no hole and no ordering error. The stained window glows with the garden behind it. The pond fades to olive haze within 30 cm, and the stream visibly runs downstream with changing ripples (checked over time in play).
- **From the spawn, and from behind:** every exhibit sign sits on a dark board below the gallery plaques seen behind it, clear of the others and of the player's own body, and carries its words on both faces, so it also reads from the far side where the editor camera often stands.
- **Shared with every player:** in play, `Materials.apply` on the pond and a window swapped them (pond to toon water, clear pane to stained glass) through the library's shared channel, and `GlassSurface` followed with the 500 mm pane mesh. `Materials.clear` put both back.
- **Frame rate (desktop editor, in play, gallery and exhibits):** 47 ms average frame, of which 30.5 ms is the engine's `gather_and_send_deltas.settle_loans` working through the gallery's local swatch entities (gh#20750) and 8.6 ms CPU render. The materials themselves add only their draws.
The test suite reruns the quick parts:
```luau
asset.resolve("glassLiquidsMaterials.tests.glassLiquidsMaterials", "testSuite"):run()
```
It covers the 25 names and every tag, the filters, costs and budget, the physical values and draw order, normal direction, seams and shimmer on one material per drawing path (stained glass, flowing water, ruby, toon water), apply / swap / clear, `GlassSurface`, the references, the picker, all 25 on one gallery ring, and this README. Each test runs inside the library's `Sync.isolate()`, so nothing it does reaches other players.
## Multiplayer
- **Applied materials are shared.** `Materials.apply` and the picker go through the library's shared channel: every player sees the material on any object every machine holds (a `synced` object, a scene's own objects, the gallery's swatches), and players who join later get it too.
- **Display pieces follow.** `GlassSurface` makes the same mesh swap on every machine from the shared material.
- **Animation is the same everywhere.** The one moving material, `waterFlowing`, is computed from the world's shared clock, with nothing sent per frame; every other material is still.
- **Lighting looks are shared.** The gallery's toolbar switches them for everyone. **Gallery pieces are local**: swatches and reference boards are built on each machine.
## Known limits
Every limit here points to its open issue on the engine's public tracker: `gh#N` is https://github.com/OrigoZero/zero/issues/N.
- **The gallery's frame rate is held down by the engine (gh#20750).** Most of the frame is the engine settling the gallery's local swatch entities (above). The materials are not the cost.
- **Labels read from the front only (gh#21224).** The engine draws 3D text on one face. The exhibit signs carry their words on both faces; the library's own gallery plaques read blank from outside the ring.
- **A second player or a browser can't be started from the editor (gh#20946).** Sharing with other players was checked through the library's shared channel from one engine, and the browser numbers came from a browser tab opened by hand.
- **Test runs wait a while (gh#19849).** The suite's render checks take most of its time; the call runs as a background task.
### Design notes
How the package behaves by design; none of these is a defect.
- **A body shows the opaque room, not another see-through object behind it.** The engine hands every see-through surface the same picture of the opaque scene, so a ruby behind a sapphire shows the room through the sapphire, not the ruby. Shells avoid it; bodies accept it (see "Shells and bodies").
- **Colour depth for gems and crystal is set for a real stone.** A ruby's colour is the colour of about 8 mm of corundum (a 60 mm crystal for quartz), whatever the size of the object wearing it. A 1 m ruby sphere would be black in reality, and the library's material should read as ruby on any shape.
- **Paths are capped.** Water looks at most 6 m deep (`waterStill`), 1.5 m (`waterFlowing`) or 2 m (`waterMurky`), ice 0.4 m, honey 0.1 m. A wall of water in front of open sky would otherwise read as an ocean.
- **One repeat per primitive face.** The engine's cube, sphere and cylinder carry UVs 0..1 per face, so a textured material shows at real size on them only when the object is its `tileSize` across. The gallery's floor tiles and walls, the display meshes, and any mesh with UVs in metres ÷ tileSize show real size.
- **The stream flows along V.** Turn the mesh's UVs to turn the current. Facets of the procedural gems are attached to the UVs, so they turn with the object.
- **Diagnostic views.** `clearMedium` hands the engine its reflection surface, so the albedo pass reads black for clear media (their colour is absorption, not albedo) and the roughness and normal passes read the real surface. `toonClear` lights itself and hands the engine its surface as well, so the toons' albedo pass shows their tint, roughness and metallic read their real values (metallic 0) and the normal pass reads the surface.
- **Colours are written as flow lists.** The engine's material loader reads a colour only as `[r, g, b, a]`; `GlassCatalog.build()` always writes that form.
## Version history
- **1.0.5 (2026-10-26):** text only. No material, shader behaviour, code or scene changed.
- **Package description:** says what a creator gets (the 25 materials for the Swatchbook material library), how to install it (one package from ZeroMind, which brings materialLibraryKit 1.3.1 and nothing else) and how to apply one by name.
- **1.0.4 (2026-10-26):** text only. No material, shader behaviour, code or scene changed.
- **No long dashes left:** the comments in `clearMedium`'s properties and shader, the `GlassShapes` description and the start scene's build and entry scripts use ordinary punctuation.
- **Package description:** states the current download (20 MB, 14 MB of it reference pictures).
- **1.0.3 (2026-10-26):** a clean install and a current README. No material, shader or behaviour changed.
- **No copies of engine parts.** 1.0.2 recorded the engine parts it uses (`Model`, `Text3D`, `Light`, the `pbr` and `unlit` shaders, the PBR lighting, shadow and screen reflection shader modules, the yaml module, three engine materials and the cube mesh) at the engine version it was built with, so installing it into a world on a newer engine also laid private copies of them under `/source/deps/`. 1.0.3 names them with no engine version: an install brings this package and materialLibraryKit and nothing else, and uses the engine your world runs.
- **README:** where materialLibraryKit lands, the current download size (610 files, 20.2 MB), the diagnostic views as the toons draw them since 1.0.2 (their own surface, not magenta), and Known limits listing only limits that have an open issue.
- **1.0.2 (2026-10-26):** brought in line with the request. **Renamed materials: update your code** if it used the 1.0.0 / 1.0.1 names.
- **Names as asked:** `glassBottleGreen` is now `glassTintedGreen`, `glassTintedBronze` is `glassTintedSmoke`, `slimeGreen` is `slime`.
- **Only `waterFlowing` moves.** Still and murky water, slime, toon water and toon gem are still (their ripple and sparkle patterns hold), and the catalog tags only `waterFlowing` as `animated`. `toonClear` gained an `animate` property (0 holds it still).
- **Optical numbers on every row.** Each description opens with the index of refraction and what the material absorbs to over what distance; tags add `ior-<n>` and `absorb-<distance>`; collection.yaml adds `ior`, `absorbColor`, `absorbDistance`, `absorbModel`. Candle wax states its IOR (1.44). The gallery plaques show the optics in place of the metal row.
- **Nothing is metallic.** The stained glass's lead came and the wired glass's wire are dark non-metallic parts (metallic 0).
- **Toon materials show their surface** in the albedo, roughness, metallic and normal views instead of magenta.
- **Exhibits:** a frosted pane joins the clear and stained panes in a wider window wall; the clear and cloudy ice blocks sit on the pond's rim, and the six gems and the quartz point stand on a stone pedestal beside the pond.
- **README:** a paste-as-is example that puts green tinted glass on a cube and lists the `translucent` category, the summary without long dashes, the table with absorption, and Known limits citing only public GitHub issues.
- **1.0.1 (2026-10-26):** checked in a web browser, and faster there.
- **Measured in a web browser.** The frame rate on every gallery step, the shimmer check at the browser's own sizes, and a per-player report (`lookingGlass` in each player's room report) saying whether both shaders compiled on that machine and whether any Looking Glass surface draws with anything but its own shader. In the browser: compiled, none.
- **Water and gems are cheaper.** The ripples' slope is now computed exactly in one evaluation instead of five, and the reflection each smooth surface traces takes at most 24 steps over 20 m instead of 48 over 40 m. Close-ups in the browser went from 19.7 to 25.3 fps (flowing water) and 20.8 to 25.9 fps (ruby beside stained glass). The look is the same: every material still passes the seam and shimmer checks, `waterFlowing`'s shimmer score fell from 0.74 to 0.58.
- **Wired glass: seam score 20.46 → 0.** The wire grid sat exactly on the repeat edges, so the seam check compared wires with clear glass and gave a large score (it still passed, on its rule that an edge no stronger than the pattern's own lines is no seam). The grid now sits half a square in, so the edges fall in clear glass and the score measures the wrap: 0. The README explains what the seam score means.
- **Exhibit signs read from both sides**, so they are no longer blank dark boards from behind (where the editor camera often opens). The stream's sign had faced away from the spawn since 1.0.0; it now reads from the spawn, at the near end of the stream, clear of the pond's sign.
- **1.0.0 (2026-10-25):** first release: 25 materials, `clearMedium` and `toonClear`, reference pictures, real-size display pieces, the gallery start scene with windows, a table, gems, a pond and a stream, and the test suite.
▲ 0↑ born
backing path · glassLiquidsMaterials.package/materials/waterFlowing.material
Problems
Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.
0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up
agent_score is exposed.usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor
+ quality × 0.35
+ performance × 0.25
± compat factor
Usability ratings
Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".
—%no reports yet
Sign in to report whether this part worked for you.
Discussion
Scoped to this part · feeds back into the world's score.
0comments
Sign in to post.sign in
No comments yet. Be the first.