I made a small web piece: a glass pyramid floats over a night sky and bends the Milky Way through it, with a rainbow coming out the other side. Tap the prism and a two-digit number appears.
Try it: https://prismtect.com/?r=devto (browser or phone, no sign-up)
The glass
The pyramid is a real 3D body in three.js with MeshPhysicalMaterial: transmission 1, index of refraction 1.5, dispersion 0.4, roughness 0. It sits in a square WebGL canvas over a 2D canvas that draws the sky and the light.
Dispersion was the first surprise. At 5 every star behind the glass split into a row of coloured specks; near real crystal, 0.4, the stars stay stars and only bright edges show colour.
Rounded edges without a toothed band
A sharp tetrahedron read as an outline, not an object. The rounded body is the convex hull of a small sphere around each corner of a slightly smaller core (ConvexGeometry). The trick is the normals: each point's normal points away from the nearest point of the core, so the four big faces stay exactly flat and each edge turns smoothly from one face to the next.
My first rounding used the hull's own flat facets, each a few pixels wide. They caught the light one by one and read as a toothed band along every edge. With the normals from the core, the light runs along an edge as one smooth line.
18 fps on a Galaxy S21, and the fix
The glass needs the night behind it to refract. I first copied the live 2D canvas into a texture every frame. On a PC that was fine; on a Galaxy S21 it held the page to 18 frames a second.
Now the still part of the night it covers (sky, Milky Way, land, sea) is copied once per size. The glass refracts the real night, sharp through clear glass, and the phone runs smoothly. The cost: moving specks and the rainbow are no longer seen through the glass.
Stack: React, three.js for the glass, plain 2D canvas for the sky and the light.
I would love to hear where it stutters on your device.
(I used an AI assistant for parts of the code and this write-up.)
Top comments (0)