Transfer-matrix method for multilayer thin-film optics, with exact analytic derivatives.
Takes a stack of layers and returns reflectance, transmittance and absorptance for absorbing and dispersive materials, at any angle of incidence, in s and p polarization. Alongside the spectra it returns the exact thickness Jacobian, the exact thickness Hessian, and the needle-insertion P-function, computed analytically rather than by finite differences.
Ships as JavaScript, as C, and as a WebAssembly build of the C. The JavaScript has no dependencies and works on import. WebAssembly is opt-in and roughly an order of magnitude faster.
npm install tmmcoreThe .wasm is prebuilt and included, so no Emscripten toolchain is required.
import { tmm } from 'tmmcore';
// A quarter-wave MgF2 layer on glass, at 550 nm, normal incidence.
const { R, T, A } = tmm(
550, // wavelength, nm
0, // angle of incidence, degrees from normal
's', // polarization: 's' or 'p'
[1.0, 0], // incident medium, ñ = [n, k]
[1.52, 0], // substrate
[{ n: [1.38, 0], d: 550 / (4 * 1.38) }] // quarter wave, thickness in nm
);
console.log(R); // 0.012600790214630274Layers run from the incident medium toward the substrate.
Mismatched conventions are the most common cause of two TMM codes disagreeing, so check these first.
| Refractive index | ñ = n + i·k, with k ≥ 0 for absorbing media |
| Time factor | exp(−iωt), so a wave exp(i(kz − ωt)) decays for k > 0 |
| Wavelength, thickness | nanometres |
| Angle | degrees from normal |
| Complex numbers | [re, im] pairs |
| Layer order | incident medium → substrate |
This is the complex conjugate of Macleod's convention. R, T and A are identical under conjugation; phase-sensitive quantities are not.
Three commands, none needing anything but Node:
node examples/01-single-layer.mjs # matches the closed-form solution
npm test # the JavaScript and the C agree
npm run compare # and both agree with an independent implementationThe first tests the equations rather than agreement, and is the only one here that does. A single quarter-wave layer at normal incidence has an exact solution (Macleod §3.2); tmmcore reproduces it to 1.4e-17, inside double-precision epsilon of 2.2e-16.
The second drives both implementations with identical inputs across absorbing, dispersive and oblique-incidence cases and compares every returned quantity. 64,416 comparisons, worst disagreement 4.4e-16. This is two implementations by the same author, so it catches porting bugs and establishes nothing beyond that.
The third checks them against Steven Byrnes'
tmm, written independently in Python under
the same complex-index convention, so only the mathematics is under test. 12,352
values, worst disagreement 8.6e-14, which is float64 accumulation noise over a
forty-layer matrix product.
Validation sets out what each level does and does not establish, and lists what is not tested at all.
The comparison with four other TMM packages covers accuracy as well as speed, including which of them run in single precision. Reproducing the timings needs a Python environment; the accuracy table does not.
src/tmm_kernel.c is C99 with no dependencies beyond libm. Drop it into a
project and compile:
cc -std=c99 -O2 -c src/tmm_kernel.cMIT © Andrey Achapovsky
Built for and used by TFStudio, an open-source optical coating design application.