npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@eleoptics/plots

v0.4.0

Published

ELEO's optical plot renderers: lens layouts, spot diagrams, ray fans, PSF and wavefront maps, MTF and more. Framework-free, themed by @eleoptics/tokens.

Readme

@eleoptics/plots

Optical plots for the web, without a framework: lens layouts in 2D and 3D, spot diagrams, ray fans, PSF and wavefront maps, MTF, field curvature, distortion and chromatic focus shift, plus ELEO's optical icons. Geometry is exact and true to scale; colors come from @eleoptics/tokens, so every plot follows the light or dark theme.

npm install @eleoptics/tokens @eleoptics/plots
import '@eleoptics/tokens/tokens.css';
import '@eleoptics/plots/plots.css';
import { layout2D, spot, map2D, gradient } from '@eleoptics/plots';

layoutEl.innerHTML = layout2D({ data: system, colorBy: 'field', rays: 'fan' });
spotEl.innerHTML = spot({ data: system });
map2D(psfCanvas, { data: system, kind: 'psf', scale: 'log' });
colorbarEl.style.background = gradient('ember');
  • SVG renderers (layout2D, spot, throughFocus, rayFan, curve, legend, icon) return markup whose colors are CSS variables: one drawing works in both themes.
  • Canvas renderers (map2D, layout3D) read the variables when they draw: call them again after a theme or palette change.
  • Data: pass your traced system as data. To try things out, import '@eleoptics/plots/sample' makes a traced achromat the default (190 KB, so it is opt-in).
  • Fonts: Fira Sans and Fira Code. Load them yourself.

Lens layouts

layout2D draws any sequential system recorded in this format (eleoptics.com's scripts/layout.py writes it). Every length is in mm, in the YZ section:

{
  surfaces: [{ z, sd, stop, image, glass, profile }],  // glass: null | 'crown' | 'flint' | { name, nd, vd }, the material after the surface
  rays: [[[[z, y], ...], ...], ...],                    // rays[fan][ray] is a polyline
  chief: [3, 3, 2],                                     // optional: one ray index per fan
}

profile is the surface's section as [z, y] points. data is a recorded layout, or a system whose layout (colored by field) and layoutWl (colored by wavelength) are recorded layouts; colorBy picks which one.

  • glass: 'crown' and 'flint' fill with --glass-crown and --glass-flint. A { name, nd, vd } glass gets its own fill inside the glass-blue band (--glass-band-hi to --glass-band-lo): lower vd sits toward flint, the same name gets the same fill within a drawing, and up to 8 distinct glasses stay distinct. A fill depends on the drawing's whole glass set, so one glass can fill differently in designs with different glasses. The fill is a CSS color function on style, resolved per theme with no redraw.
  • Chief ray: chief[k] for fan k; without chief, the fan's middle ray, floor(n / 2). That is eleoptics.com's rule, and it can be wrong for a vignetted fan, so record chief when you know it. An index outside the fan throws an error naming the fan.
  • box: the {zmin, zmax, ylo, yhi} the drawing shows, in mm. It defaults to layoutBounds([layout]). Drawings with the same box, width and labels share one scale, so they compare true to size.
  • layoutBounds(layouts): the box that frames every layout given: z covers the rays, y covers 0, the rays and every surface edge but the image's, padded 4%.
  • glassLegend(data): legend keys for a layout's { name, nd, vd } glasses: one eleo-key eleo-key--swatch span per distinct glass the drawing fills, by name, in order of first use, its swatch in that glass's polygon fill. data is a recorded layout or a system carrying one in layout, else layoutWl. Shorthand 'crown'/'flint' and a glass on the last surface (which draws nothing) get no key; with no named glass it returns ''.
  • labels: one text per fan, drawn 6 px past the image plane, beside the end of that fan's chief ray, with room kept for them (6 px and 6 px a character of the longest, on the right). A fan with no rays gets none. A null entry skips that fan; more labels than fans throws. A width that the 4 px pad and the label room fill throws.
  • marks: false drops the STO and IMA labels. Default true.
import { layout2D, layoutBounds } from '@eleoptics/plots';

const box = layoutBounds([before, after]);
const labels = ['0°', '12°', '24°'];
beforeEl.innerHTML = layout2D({ data: before, box, labels, marks: false });
afterEl.innerHTML = layout2D({ data: after, box, labels, marks: false });

Migration: data.layout is a recorded layout

This is a breaking change. data.layout used to be a bare rays array (layout[field][ray] of [z, y] points), drawn against data.profiles and data.zimg. It is now a recorded layout, {surfaces, rays, chief?}, and so is data.layoutWl (previously {field, rays}). Record your system in the format above: each lens surface becomes a surfaces entry with its profile and the glass after it, the stop and the image become surfaces with stop or image set, and the old rays array moves to rays. layout2D throws on the old shape. layout3D is unchanged: it still reads profiles and rays3d.

Palettes

Plots color index 1 to 8 in the standard order. For readers with color vision deficiency, set a palette on the page:

<html data-palette="red-green">

The values are standard (the default), red-green (protanopia and deuteranopia) and blue-yellow (tritanopia). Use one palette per page, on <html>. SVG plots recolor at once; canvas plots (map2D, layout3D) redraw as on a theme change.

--series-1 to --series-8 are the index colors for any chart on the page. They follow the palette and theme. The token number is not the index.

Physics helpers

import { airy, airyRadius, j1, slabMode, colormap } from '@eleoptics/plots/physics';

airy(r, 0.5876, 4);      // Airy intensity at r µm for f/4 at 587.6 nm, peak 1
airyRadius(0.5876, 4);   // 2.87 µm: first dark ring, 1.22 λN
slabMode(y, 5);          // TE0 mode of a slab waveguide with a 5-unit core half-width
colormap('ember');       // 256 [r, g, b] entries in the current theme

Without a bundler

<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="plots.css">
<script src="eleo-plots.js"></script>         <!-- window.ELEO -->
<script src="eleo-plots-sample.js"></script>  <!-- optional: the sample system -->

eleo-physics.js is the physics helpers alone (1 KB). eleo-layout.js is layout2D, layoutBounds and glassLegend alone, with no sample, for drawing recorded layouts. It styles its own texts, so tokens.css is all it needs (a size test caps its growth at 2 KB gzipped per roadmap row).

For Svelte, see @eleoptics/plots-svelte. MIT licensed. Part of eleo-ui.