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

@three-flatland/normals

v0.1.0-alpha.1

Published

Offline + runtime normal map generation for sprites and tilesets. Contributes a `normal` subcommand to the flatland-bake CLI.

Readme

@three-flatland/normals

Offline + runtime normal map generation for sprites and tilesets. Contributes a normal subcommand to the flatland-bake CLI and ships a browser-safe NormalMapLoader used by three-flatland.

Alpha Release — this package is in active development. The API will evolve and breaking changes are expected between releases. Pin your version and check the changelog before upgrading.

npm license

Install

npm install @three-flatland/normals @three-flatland/bake

Requirements

  • three >= 0.183.1 (peer, WebGPU/TSL)
  • @three-flatland/bake — CLI dispatcher that hosts the normal subcommand

Quick Start

Bake a normal map from the CLI

npx flatland-bake normal public/sprites/knight.png

Writes public/sprites/knight.normal.png next to the source. Ship the baked PNG; the runtime loader picks it up automatically.

Load a normal map at runtime

With three-flatland, pass normals: true to any texture-producing loader — the baked sibling is discovered and attached for you. Outside three-flatland, use NormalMapLoader directly:

import { NormalMapLoader } from '@three-flatland/normals'

const normalMap = await NormalMapLoader.load('/sprites/knight.png')
if (normalMap) material.normalMap = normalMap

NormalMapLoader resolves the .normal.png sibling via HEAD probe and returns null when it's missing so you can fall back to an unlit material or a runtime bake.

Options

CLI flags

flatland-bake normal <input.png> [output.png] [--strength <n>]
flatland-bake normal <input.png> --descriptor <input.normal.json>

| Flag | Description | |---|---| | --strength <n> | Gradient multiplier before normalization (default 1) | | --descriptor <path> | Region-aware descriptor JSON — per-frame / per-tile control for atlases and tilemaps |

See the three-flatland docs for descriptor examples covering sprite sheets, LDtk tilesets, and directional (3/4-view wall) tiles.

Loader options

// Generate this asset's normal map in the browser on every load instead
// of loading a pre-baked sidecar. You still get the same normal-map data;
// this flag just commits to "the browser is where it's produced." For
// procedurally varied content, throwaway prototypes, lean bundles.
// Not a dev-iteration knob — the default path (probe → bake on miss +
// warn) already handles iteration. Mirrors `SlugFontLoader.forceRuntime`.
const tex = await NormalMapLoader.load(url, { forceRuntime: true })

Authoring tiles for LDtk / Tiled

Per-tile custom data drives the tileset's baked normals. The LDtkLoader and TiledLoader read these fields from each tile's properties panel and synthesize regions for the baker.

Atlas encoding (what's in the output PNG)

| Channel | Meaning | |---|---| | R | nx — normal X component, [-1, 1][0, 255] | | G | ny — normal Y component, [-1, 1][0, 255] | | B | elevation — world-space Z in [0, 1], 0 = ground, 1 = top-of-wall | | A | source alpha (silhouette) |

Runtime reconstructs nz = sqrt(max(0, 1 − nx² − ny²)) — outward-facing convention, no sign ambiguity.

Direction vocabulary

'flat'                                        — no tilt, normal = (0, 0, 1)
'up'    | 'north'     — tilts toward top of screen
'down'  | 'south'     — toward bottom
'left'  | 'west'      — toward left
'right' | 'east'      — toward right
'up-left'    | 'north-west'    — diagonals at π/4
'up-right'   | 'north-east'
'down-left'  | 'south-west'
'down-right' | 'south-east'
<number>              — raw radians, 0 = +X, CCW positive

Aliases are equivalent; use whichever reads better for your team.

JSON recipes

Standard floor tile

{}

Or leave untagged. Default elevation 0, flat normal, torch-lit.

All-cap tile (wall top viewed dead-on, roof patch, pillar cap)

{"tileElevation": 1}

Whole cell is flat at cap height. Torch-dark from ground-level lights, ambient-lit only.

Top-of-map wall (cap at top of art, face below)

{"tileDir": "south", "tileCapTop": 4}

Cap auto-elevation = 1, face auto-elevation = 0.5.

Bottom-of-map wall (cap at bottom of art, face above)

{"tileDir": "north", "tileCapBottom": 4}

Side walls (cap strip runs along one vertical edge of the art)

{"tileDir": "east",  "tileCapBottom": 4}    // left-of-map wall
{"tileDir": "west",  "tileCapBottom": 4}    // right-of-map wall

Outer corners (L-shaped cap + diagonal face)

{"tileDir": "south-east", "tileCapTop": 4, "tileCapLeft": 4}     // NW corner
{"tileDir": "south-west", "tileCapTop": 4, "tileCapRight": 4}    // NE corner
{"tileDir": "north-east", "tileCapBottom": 4, "tileCapLeft": 4}  // SW corner
{"tileDir": "north-west", "tileCapBottom": 4, "tileCapRight": 4} // SE corner

Outer-facing corner (small square cap, face wraps around as L-shape)

{"tileDir": "south-east", "tileCapTopLeft": 4}

Four variants: tileCapTopLeft / tileCapTopRight / tileCapBottomLeft / tileCapBottomRight. Face auto-decomposes into 2–3 rectangles around the corner cap square.

Half-wall / low partition

{"tileDir": "south", "tileCapTop": 4, "tileElevation": 0.3}

Face sits lower on the wall so ground-level torches light the face more directly. Cap still at 1.

Tall pillar / raised architecture

{"tileDir": "south", "tileCapTop": 4, "tileElevation": 0.9}

Face sits near cap height — torches barely light the face, reads as extremely tall.

Brick/stone surfaces with cracks sunken (non-alpha bump)

{"tileDir": "south", "tileCapTop": 4, "tileBump": "luminance", "tileStrength": 1.5}

Bright brick faces raise, dark mortar sinks. Works on opaque tiles where alpha is 1 everywhere.

Per-tile custom data field reference

| Field | Type | Default | Meaning | |---|---|---|---| | tileDir | direction alias or radian | 'flat' | Which way the face tilts | | tileDirection | direction alias or radian | — | Alias of tileDir | | tileCap | px | 0 | Shorthand — same as tileCapTop when no per-edge field set | | tileCapTop / tileCapBottom / tileCapLeft / tileCapRight | px | 0 | Edge-strip cap thickness | | tileCapTopLeft / tileCapTopRight / tileCapBottomLeft / tileCapBottomRight | px | 0 | N×N corner-square caps | | tileElevation | 0..1 | 0.5 on face / descriptor default elsewhere | World-space Z of the primary surface | | tilePitch | radians | π/4 | Tilt angle from flat | | tileBump | 'alpha' / 'luminance' / 'red' / 'green' / 'blue' / 'none' | 'alpha' | Per-texel bump source | | tileStrength | float | 1 | Gradient multiplier (negative inverts) |

Legacy *Px aliases (tileCapPx, tileCapTopPx, etc.) are still accepted.

Using with plain Three.js

This package is renderer-agnostic and works with any Three.js WebGPU project. Bake a normal via the CLI, load it with NormalMapLoader, and assign it to a standard material's normalMap slot. No three-flatland dependency required.

Related

  • three-flatland — the 2D engine. High-level loaders (SpriteSheetLoader, LDtkLoader) orchestrate this package automatically when you pass normals: true.
  • @three-flatland/bake — shared bake pipeline infrastructure. Hosts the flatland-bake CLI; other packages plug in their own bakers.

Documentation

Full docs, interactive examples, and API reference at thejustinwalsh.com/three-flatland

License

MIT


This README was created with AI assistance. AI can make mistakes — please verify claims and test code examples. Submit corrections here.