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

scenic-draft-extras

v0.22.1

Published

Extra scenic-draft primitives and materials — hexagonal and triangular prisms, chain links, quadrilateral faces and fifty-odd more surface presets — written against the library's own extension points.

Readme

scenic-draft-extras

Five shapes scenic-draft no longer carries and fifty-odd materials it never did — both written against what the core library already offers rather than against a change to it.

import { backgrounds, materials, paint, render, rotateY, translate, union } from 'scenic-draft';
import { hexPrism, link, triPrism } from 'scenic-draft-extras';

render(canvas, {
  background: backgrounds.dusk,
  scene: union(
    paint(hexPrism(0.7, 0.9), materials.brass),
    translate(rotateY(triPrism(0.5, 0.4), Math.PI / 6), [1.8, 0, 0]),
    translate(link(0.5, 0.15, 0.35), [-1.8, 0, 0]),
  ),
});

Each of these was a primitive of the core library's own, with a case in its compiler and a variant in its SceneNode union. They live here instead because the shapes a scene might want are endless and a compiler's switch is not: a hexagonal prism, a triangular prism, a chain link and a quadrilateral face are all worth having and none of them is load-bearing. So they are also the proof that custom() is enough to add a primitive with — the distance function, the validation and the name, in a package a scene reaches for only if it wants one.

Nothing about them is special from the core library's side. Every builder returns a plain SceneNode, so the whole vocabulary above it works untouched: translate, rotate, repeat, paint, the smooth booleans, scenic-draft/probe's checkField, and scenic-draft-standalone's exporters.

extraMaterials is the same argument made about surfaces. materials in the core library is short on purpose — every name in it is one a reader has to be able to hold in their head — and a preset is plain data, so a second library of them needs no change to anything:

import { paint, sphere } from 'scenic-draft';
import { extraMaterials } from 'scenic-draft-extras';

paint(sphere(1), extraMaterials.malachite);

Install

npm install scenic-draft-extras
# or: pnpm add scenic-draft-extras

The package carries scenic-draft and scenic-draft-fluent at exact versions, so the compiler that reads these fields is the one they were written for. It ships ESM with TypeScript declarations.

The shapes

| | | | --- | --- | | hexPrism(radius, height) | A hexagonal prism along the y axis. radius is the apothem — centre to the middle of a flat — and height the half-length along the axis. | | triPrism(radius, height) | An equilateral triangular prism along the y axis. radius is the circumradius, so the sides are radius * √3 long; the cross-section sits in the x–z plane with one corner along +z. | | link(major, minor, height) | A chain link: a torus stood on edge in the x–y plane and drawn apart along y. The straight part is 2 * height long and the whole link 2 * (height + major + minor). | | quad(a, b, c, d, thickness) | A quadrilateral face through four corners given in order around it, thickness out from their plane on each side. | | quads(faces, thickness) | A whole list of those as one shape: a folded plate, a set of panels, a quad mesh. |

Every measure is a half-measure, as everywhere in scenic-draft: a height is the half-length, a thickness the half-thickness. A face is a sheet 2 * thickness thick with its edges and corners rounded to that radius, since what is measured is the distance to the face itself.

A quad has three promises to keep that a triangle does not — its corners go round it in order, they lie in one plane, and it turns the same way at every corner. All three are checked when you build it, because Quilez's quad field assumes rather than handles them. A face that fails any of them is two triangles, and scenic-draft's own triangles takes it with the field still exact.

The materials

extraMaterials is a second preset library, in the shape of the first one: an object of finished Material values that paint() takes.

| | | | --- | --- | | Metals | bronze, titanium, platinum, roseGold, gunmetal, mercury, bluedSteel, tarnishedSilver, rustedIron, verdigris | | Stone | granite, slate, sandstone, limestone, basalt, onyx, lapisLazuli, malachite, turquoise | | Gemstones | ruby, sapphire, amethyst, aquamarine, opal | | Grown | bone, ivory, coral, bark, cork, moss | | Cloth | wool, silk, corduroy, suede | | Manufactured | bakelite, acrylic, vinyl, carbonFibre, brick, asphalt, paper | | Cold and edible | ice, snow, milk, honey, chocolate | | Lights | neon, ember, lava | | Thin films | soapBubble, beetle, compactDisc |

Each one is a complete Required<Material>, exactly as a core preset is: every optional behaviour is spelled out, including the ones switched off, so a preset dropped inside a patterned or transmissive paint() comes out as itself rather than inheriting something it never asked for. They mix with the core library's own — materials.mix(extraMaterials.bronze, materials.iron, 0.4) — and spreading one to change a field is the expected way to depart from it.

There are no builders here that take a colour. materials.metal, .lacquer, .wax, .velvet and the rest already cover that half of the problem, and a bronze that took a colour would be metal with a longer name. What a library can add is names.

Anything with transmission or subsurface above 0 — the gemstones, the ice, the honey, the milk — wants a bounces budget of 8 to 12 rather than the default 6, like materials.glass and materials.jade before them. And the films (beetle, soapBubble, compactDisc, bluedSteel, opal) have their colour in the reflection rather than in the pigment, so they want an environment with some structure to reflect: a gradient with a sun rather than a flat colour.

EXTRA_MATERIAL_NAMES is the names in the order they are declared, for a menu or an index.

Two entry points

scenic-draft-extras returns plain nodes, for a scene written in scenic-draft's nested calls. scenic-draft-extras/fluent returns chainable shapes, for one written in scenic-draft-fluent's chain:

import { backgrounds, draft, materials, sphere } from 'scenic-draft-fluent';
import { hexPrism } from 'scenic-draft-extras/fluent';

draft(hexPrism(0.7, 0.9).rotateY(Math.PI / 6).paint(materials.brass).union(sphere(0.4)))
  .render(canvas);

They build the identical node — a fluent Shape is a SceneNode, with the chaining methods on its prototype — so the two mix freely in one scene. There is little else in the fluent entry point: the vocabulary a scene is written in stays scenic-draft-fluent's, and this package adds five leaves to it rather than wrapping it again. extraMaterials is re-exported from it unchanged, since a material is data either dialect paints with and there is no chain to put it on.

Reading one back

By the time a shape is in a tree it is a custom node like any other, which is what makes it compile and export like one — and what makes it opaque to anything that wants to show a scene rather than trace it. readExtra(node) says which of these shapes a node is and what it was built with, or null for anything else:

import { hexPrism, readExtra } from 'scenic-draft-extras';

readExtra(hexPrism(0.7, 0.9)); // { name: 'hexPrism', radius: 0.7, height: 0.9 }
readExtra(sphere(1)); // null

It is recognition rather than a guess: the function's name and its source have to be this package's, so somebody's own sdLink is not mistaken for a link. That is what a source printer or a scene editor needs, and it is why the site's guide can show hexPrism(0.7, 0.9) beside a render of one rather than fourteen lines of GLSL.

The GLSL

HEX_PRISM_GLSL, TRI_PRISM_GLSL, LINK_GLSL and quadFacesGlsl(count) are the source each shape is made of, exported because it is the interesting part. They are Inigo Quilez's distance functions, unchanged from the ones scenic-draft's compiler used to write inline, and they are a working example of what a custom primitive looks like — including, in quadFacesGlsl, one that takes a variable number of arguments by naming a function per arity.

Licence

PolyForm Noncommercial 1.0.0, like every package in this repository. Free for noncommercial use; a commercial licence is a separate conversation. See LICENSE in the package.