Rock slices blaze with interference colors under a polarizing microscope.
A slice of rock ground to 30 micrometers is transparent, and each mineral grain splits light into two rays that fall out of step by its birefringence times the thickness. Between crossed polarizers that retardation becomes a color: the program integrates sin^2(pi R / wavelength) against the CIE color matching functions to build the Michel-Levy chart, scales it by sin^2 of twice the grain's extinction angle, and uses a Jones-calculus table when the gypsum plate is in. The four slides are generated: a domain-warped, anisotropic Voronoi diagram for rounded grains, painted rectangles for feldspar laths and mica flakes, and per-pixel optics for albite and Carlsbad twins, microcline tartan, undulose quartz, pleochroic biotite with zircon haloes, serpentine cracks in olivine and rainbow-fringed calcite. Every frame resamples the slide through the rotating stage, so grains blink in and out of extinction as it turns.
Try it. Drag the graduated ring (or use the arrow keys) to turn the stage, and drag inside the view to move the slide. Toggle crossed polars (X) and the gypsum plate (G), switch rocks with the buttons or keys 1 to 4, press up or down to zoom, and point at a grain to identify it on the Michel-Levy chart.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a polarizing microscope view of a rock thin section with JavaScript and the HTML canvas element. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Make a canvas that fills the window on a dark background and draw a large circular field of view in the middle, like looking down a microscope eyepiece.
- Write a function that turns a retardation in nanometers into an interference color. For wavelengths from 380 to 780 nm, the light that gets through crossed polarizers is sin^2(pi * R / wavelength). Weight it with an approximation of the CIE x, y, z color matching functions, convert XYZ to linear sRGB, white balance so a flat spectrum is white, and gamma encode. Precompute a table from 0 to 2000 nm and draw it as a strip first: black, gray, white, yellow, red, then blue, green, and paler bands. That is the Michel-Levy chart.
- Make a mineral texture with a Voronoi diagram of about 150 random seeds on an offscreen grid. Give each grain a retardation (birefringence times 30,000 nm, times a random factor) and an extinction angle.
- Each frame, color every pixel inside the circle with the table at its grain's retardation, multiplied by sin^2(2 * (angle + stage)), and slowly rotate the stage so the grains blink dark four times per turn.
Once that works, make it beautiful:
- Rotate the texture with the stage by sampling it through a rotation, and draw graduations around the field that turn with it.
- Add a few mineral types with different birefringence (quartz gray, olivine bright, calcite pastel), stripes of alternating angle for twinned feldspar, and a toggle for plane light.
- Let the user drag to turn the stage and hover a grain to show its name and retardation.
Explain the optics in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as a gypsum plate using two-retarder Jones calculus, pleochroic biotite, or elongated feldspar laths.