Skip to content

Normal Map Generator for Sprites — Lit Preview, Godot, Unity

Open a sprite and its normal map is already there — a bevel grown inward from the transparent edge, with detail from brightness or from your own height map — lit by a point light you can drag. The lighting uses Godot 4's own 2D formula, so the preview is what the engine shows.

Drop a sprite, a sprite sheet or a tileable texture (PNG)
Runs in your browser. Files are not uploaded.
Studio Texture workspace: a pixel-art torch cut into 6 frames, lit by a point light with its generated normal map
The real Studio with a CC0 asset (OpenGameArt).
Exports to
  • Godot 4 2D lightsVerified· Godot 4.7.2
  • Unity 6 URP 2DVerified· Unity 6000.5.3f1 (URP 17.5)
  • PNG set + JSONPlain files

What it does

  • Bevel from the silhouette: width and height in pixels, profiles round, linear, smooth, cove or ledge, and a suggested width — 1.5 px for pixel art up to 128 px, a rounded pillow for larger sprites.
  • Detail from brightness (labelled an approximation, with "Ignore shading wider than" so a dark cloak is not read as a pit), or a height map of the same size, 16-bit PNGs included.
  • Height brush (B in Texture): raise, lower, smooth, flatten or erase where the guess is wrong; every stroke is one undo step and stays inside its frame.
  • Up to 8 point lights with colour, energy, height, radius and falloff, plus ambient and specular. The WebGL2 preview matches the CPU reference within 1/255, and so did Godot 4.7.2.
  • Pixel-art normals snap every pixel to 4, 8, 16 or 32 directions with 1 to 4 tilts, for a shaded look that stays in the style of the art.

How to do it

  1. Drop a sprite PNG with transparency (a sheet is cut into frames in the Sprite workspace first).
  2. Keep the suggested bevel or change its width and profile in the Normal map panel.
  3. Drag the light around in the Lit view, press C to compare flat and lit, and paint relief with the Height brush where it reads wrong.
  4. Export (Ctrl+E) the Godot 4 scene, the Unity 6 URP 2D bundle, or plain PNGs in OpenGL and DirectX.

Settings to start from, by kind of picture

The Studio suggests Sprite or Tileable texture from the transparency; these are the usual starting points.

Your pictureStart withThen check
Pixel-art sprite (frames ≤ 128 px)Bevel 1.5 px, round profile, Clamp edges; Pixel kernel; optionally pixel-art normalsMove a light around in Lit view; the lit edge should follow the light
Larger or painted spriteBevel ≈ 0.9 × the typical inscribed radius (a pillow); detail from brightness with "Ignore shading wider than"Dark areas should not read as holes; fix with the Height brush
Tileable texture (no transparency)Wrap edges; detail from brightness or your height mapWrap test 0 = seamless; look at Tile 3×3 (T)
An existing normal mapUse it as the source instead of generatingThe Check panel verdict, then "It is OpenGL / It is DirectX"; on sprites, Flip red (X−) if red is reported flipped

Engine exports and how each was checked

"Verified" means the exported files were loaded by the real engine or tool on real CC0 assets and the result was compared with the source. Anything that was not loaded is labelled as such.

TargetFilesCheck
Godot 4 2D lights_lit.tscn · CanvasTexture .tres · normal / specular PNGVerified Godot 4.7.2Six real CC0 cases rendered by Godot (gl_compatibility): every checked frame within 1/255 of the Studio's lit preview. The same render with green flipped is 3.8–37 levels off, so the check can see a wrong convention.
Unity 6 URP 2DPNG · nerulio-texture.json · Editor/NerulioNormalMapImporter.csVerified Unity 6000.5.3f1 (URP 17.5)12 of 12 runs (6 cases in Gamma and Linear colour space): the lighting URP applied matches the exported normal map (mean error 0.0037–0.0085). URP draws its own light falloff, so brightness is Unity's, not the preview's.
PNG set + JSONalbedo · _n (OpenGL) · _n_dx (DirectX) · 16-bit height · AO · specular · manifestPlain filesPlain files for any engine or tool; no engine run is claimed for them.

Evidence

Godot 4.7.2 rendered 6 real CC0 cases (pixel art, an HD sprite, a tileable texture, an imported DirectX map): every checked frame within 1/255 of the Studio's preview. Unity 6000.5 URP 2D: 12 of 12 runs pass (6 cases × Gamma and Linear). OpenGL/DirectX detection on real maps of known convention: 128 full 1K maps 100 % right, and not one wrong "high" verdict in 1,562 samples (a naive "green-high means OpenGL" rule gets 46 %).

Limits

  • On a picture without transparent edges (a tileable texture) a flipped red channel cannot be told apart from a DirectX green; the Check panel says so and offers Flip red.
  • A sheet is lit as one picture until its frames are cut in the Sprite workspace; the Texture workspace does not cut frames.
  • The rim light is preview only, and Unity gets no specular map (Godot does).
  • Unity draws its own light falloff: the exported normal map is verified in Unity, the brightness curve is Unity's.

Questions

Does it work for pixel art?

Yes. Frames up to 128 px get a 1.5 px rim by default, the Pixel kernel adds no smoothing, and pixel-art normals can snap the directions. Hand-painted pixel light is hard for every generator: on a CC0 torch, Nerulio, Laigter and PBR Forge all came out 37–42° off the artist's own map.

OpenGL or DirectX — which do I need?

Godot and Unity read OpenGL-style green (Y+), which is the default. The PNG set writes both, _n for OpenGL and _n_dx for DirectX. If you import an existing map, the Check panel reads its convention and changes nothing until you press "It is OpenGL" or "It is DirectX".

What does the engine export contain?

Godot 4: a _lit.tscn with a Sprite2D, a CanvasTexture, CanvasModulate for the ambient and one PointLight2D per light, plus an AnimationPlayer for frames. Unity 6: the PNGs, a JSON and an editor script that adds the map as the _NormalMap secondary texture and can create lit preview lights.

Is it free, and do I need an account?

Everything on these pages is free and works without an account. There is no watermark and no export that is held back.

Are my sprites uploaded?

No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.

Does it need Aseprite or an engine plugin installed?

No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).