# skinview3d: a Three.js Minecraft skin viewer you can embed in a page

> A TypeScript library that renders Minecraft skins, capes, elytra and ears in the browser with Three.js, with a SkinViewer object you configure from JavaScript. It ships as an npm package with type definitions and a separate build of documentation and preview bundles.

**bs-community/skinview3d** — Three.js powered Minecraft skin viewer.

- Repository: https://github.com/bs-community/skinview3d
- Website: https://skinview3d-demo.vercel.app
- Stars: 738 · Forks: 115
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/bs-community-skinview3d

## A viewer for the things Minecraft players actually show off

A Minecraft skin is a texture that gets mapped onto a small boxy model, and a cape is the same idea worn on the back. Rendering one properly is not trivial, because the texture layout, the model shape and the animation all have to agree, and because the model shape differs between the default and slim arm variants.

skinview3d is a Three.js powered Minecraft skin viewer that packages that work as a library. The listed features are 1.8 skins, HD skins, capes, ears, elytras, and slim arms with automatic model detection for slim or default. It also offers FXAA, fast approximate anti-aliasing, which is worth noting because a Minecraft model is mostly flat coloured rectangles and the edges look ragged without it.

The ears deserve an explanation, because they are unusual. The library supports two types of ear texture: a standalone 14x7 image that contains the ear, and a skin texture that contains the ear, such as the one in deadmau5's skin. The second type is the interesting one, because the ears are drawn into the player texture itself, which is how that particular skin does it.

The audience is developers building anything that shows a Minecraft character in a browser: a skin browser, a server list with previews, a marketplace or an inventory viewer. The npm package badge and the MIT licence badge are the two things a developer looks at first, and the project is on the npm registry as skinview3d.

## One SkinViewer object, configured from JavaScript

The integration surface is a single object. You construct it against a canvas element and then mutate its properties as the interface needs change.

The construction looks like this:

```js
let skinViewer = new skinview3d.SkinViewer({
	canvas: document.getElementById("skin_container"),
	width: 300,
	height: 400,
	skin: "img/skin.png"
});
```

Everything else is a property or a method on that object. Resizing is a matter of setting width and height. Loading a different skin is loadSkin with a URL, and loading a cape is loadCape with a URL. The background is set either as a plain colour value, through the background property, or as an image through loadBackground, or as a panoramic image through loadPanorama. The camera field of view is the fov property and the zoom level is the zoom property, where 0.5 zooms out.

The camera and the scene are also reachable directly, which is how lighting is configured. The camera field of view is changed with skinViewer.fov = 70 and the zoom with skinViewer.zoom = 0.5.

The result is that there is no separate scene graph for the user to assemble. The viewer owns the scene, the model and the animation loop, and the application code decides what to show and how to look at it.

## Capes, elytra and the backEquipment option

The cape API is where the library does something that a plain texture renderer cannot, because a cape and an elytra are different objects that happen to share a texture.

loadCape takes a texture and an option. Passing a cape texture with backEquipment set to elytra renders it as a pair of wings on the back instead of as a flat cape. Unloading it is done by passing null to loadCape, which hides the cape or elytra rather than tearing anything down.

So the same file can be shown two ways depending on what the user owns in their inventory, which is the difference between a viewer and an inventory-aware viewer.

The animation side works the same way, by assigning an animation object rather than by calling a method. The example assigns a WalkingAnimation, then sets its speed, then pauses it, and setting the animation property to null removes it:

```js
skinViewer.autoRotate = true;
skinViewer.animation = new skinview3d.WalkingAnimation();
skinViewer.animation.speed = 3;
skinViewer.animation.paused = true;
skinViewer.animation = null;
```

Auto-rotation is a separate boolean property, which means a viewer can spin the model on its own while an animation plays, and you can stop either independently.

## Two lights, and how to turn off the shadows

The lighting model is small and documented, which is a good sign for a rendering library.

By default there are two lights on the scene. One is an ambient light, and the other is a point light from the camera. The ambient light is the global one and the camera light is the moving one that follows the view, which is why the model is lit consistently from the front as you orbit it.

Both are reachable from the viewer, so the intensity is settable:

```js
skinViewer.cameraLight.intensity = 0.6;
skinViewer.globalLight.intensity = 3;
```

The documentation then gives the specific combination to remove shadows entirely: setting globalLight.intensity to 3.0 and cameraLight.intensity to 0.0 will completely disable shadows.

That is worth understanding, because the two lights interact. The global light with intensity 3 is bright and flat, and killing the camera light removes the directional component that casts the shadow. A skin viewer that shows a character on a flat background often wants exactly that, since a shadow on a background image is usually wrong. A viewer that shows a character in a scene wants the opposite.

The names globalLight and cameraLight are the interface, so anything else in the scene, including a background panorama, is lit by the same two lights.

## Ears come from the skin texture or from a separate image

Ears are the feature with the most moving parts, and the API has three entry points for them.

In the constructor you can set ears to current-skin, which means the ears are drawn on the skin texture already loaded, and that is the deadmau5 case. Alternatively you can pass an object with a textureType and a source, where textureType is standalone for the 14x7 image or skin for a full skin texture.

At runtime, loadSkin accepts an option with ears set to true, so a newly loaded skin can show its own ears. And loadEars loads ears from another texture entirely, with a textureType of standalone or skin:

```js
skinViewer.loadEars("img/ears.png", { textureType: "standalone" });
skinViewer.loadEars("img/deadmau5.png", { textureType: "skin" });
```

The three cases cover the situations that come up in practice. A skin that already has ears needs nothing extra, a skin that does not can be paired with a separate ear texture, and a skin that has ears in a different texture can point at it.

The reason this is a library feature rather than something a user implements is that the ear geometry has to be attached to the head bone in the right place and drawn from the right part of the texture. Once that is solved for the two formats, it is solved for everybody.

## Name tags need the Minecraft font, and that is a real dependency

Name tags are supported, and they come with a catch that is documented clearly.

The usage is a property assignment. You can set the tag to a plain string, and the documentation notes that the tag with text "hello" is set by assigning skinViewer.nameTag = "hello". You can also pass a NameTagObject with a textStyle, for example a yellow style, and unset it by assigning null.

In order to display name tags correctly, you need the Minecraft font from South-Paw/typeface-minecraft. The repository includes the font at assets/minecraft.woff2, and the documentation says to add an @font-face rule to your CSS to load it:

```css
@font-face {
	font-family: 'Minecraft';
	src: url('/path/to/minecraft.woff2') format('woff2');
}
```

So the library renders the tag as a textured plane with the right geometry, but the glyphs come from a font file you must supply and declare. If you skip that step you get a name tag with the wrong letters, which is a confusing failure because the rest of the viewer works.

The assets directory being part of the published package is the reason this is possible at all, since the files list in the package manifest includes assets alongside libs and bundles.

## How the package is built and what it ships

The build is a three part pipeline and the package manifest spells out the shape of the output.

The build script runs three tasks in parallel: build:modules, build:bundles and build:preview. The modules step runs the TypeScript compiler with tsc -p ., producing the JavaScript and the type declarations. The bundles step runs rollup with the swc3 config plugin, producing standalone bundles. The preview step runs vite build, which is the example site.

There is a separate documentation build, build:docs, which runs typedoc against src/viewer.ts. That tells you where the public API surface is declared, and it means the documentation is generated from the same source the package is built from rather than maintained by hand.

The published files list is libs, bundles and assets, so consumers get the compiled modules, the standalone bundles and the Minecraft font. The package main entry is libs/skinview3d.js and the package is an ES module, with the type field set to module.

The runtime dependencies are three: three, the renderer, @types/three, and skinview-utils, which is the companion package that handles the skin and cape parsing and the model geometry rather than the rendering. Separating the parsing from the rendering is what makes the slim arm detection and the ear handling work across texture formats.

The repository also carries an examples directory with an index.html, a main.ts, a style.css, and a separate offscreen-render.html with offscreen-renderer.ts, which is the route for rendering without a visible canvas.

The readme documents a single build command:

```bash
npm run build
```

The package version is 3.4.2 and the licence is MIT. The repository is not archived and the last push was on 2026-05-03, and there are no GitHub releases, so the npm version is the version to track.

## Where a skin viewer is the wrong tool

There are cases where this library is more than you need, and one where it may not work for you.

The obvious one is when you only want to show a flat skin image. A canvas with a 2D context drawing the texture is a few lines of code and pulls in no WebGL, no Three.js and no bundle. The library exists to give you a 3D model with animation and camera control, and if you do not need those, you are paying for a rendering engine to draw a rectangle.

The second is WebGL availability. This is a WebGL renderer, so it will not work in an environment without hardware acceleration or with WebGL disabled, and the cost of that dependency falls on you in environments you do not control, such as embedded browsers or locked down corporate desktops.

The third is texture format coverage. The feature list names 1.8 skins and HD skins, and the loader is built around the standard skin texture layout. If your users have skins in some other tool's format, or textures that use regions the loader does not expect, you will be normalising them yourself before the viewer ever sees them.

The fourth is the dependency on Three.js at a specific version range. The package depends on three at ^0.156.0. If your application already ships a different Three.js major, you are either duplicating the renderer in the bundle or forcing a shared version that may not be what your code expects.

And the fifth is the state of the project. The last push was on 2026-05-03, which is more than five months before now, with no GitHub releases in the repository. The npm registry is the release channel here, so pin the version you use and read the changelog in the npm package rather than assuming a tag exists.

## Conclusion

Adopt skinview3d if you are building a skin browser, a marketplace preview or an avatar renderer and want the model, the animation and the camera control already solved rather than hand built on top of Three.js. Skip it if you only need to display a flat skin image, since the library is a 3D viewer with a WebGL dependency. Verify first that your skin and cape textures match the formats it supports, that the Minecraft name tag font is loaded if you use name tags, and that the version of Three.js you already ship does not conflict with the one it depends on.

## FAQ

### How do I add a Minecraft skin viewer to my page with skinview3d?

Create a canvas element and construct a SkinViewer against it, passing the canvas, the width, the height and a skin URL. The readme gives a working example, and there is also a CodeSandbox template linked from the project.

### Does skinview3d support Minecraft capes and elytras?

Yes. loadCape takes a cape texture, and passing backEquipment set to elytra renders it as wings instead of a flat cape. Passing null to loadCape hides the cape or elytra.

### How do I turn off the shadows in skinview3d?

Set globalLight.intensity to 3.0 and cameraLight.intensity to 0.0. The scene has two lights by default, an ambient light and a point light from the camera, and that combination removes the shadows entirely.

### How are the ears in skinview3d loaded?

Two ear texture types are supported: standalone, which is a 14x7 image containing the ear, and skin, which is a skin texture that contains the ear such as deadmau5's. You can set ears in the constructor, pass an ears option when loading a skin, or call loadEars with a texture and a textureType.

### What do I need to display a name tag in skinview3d?

You need the Minecraft font from South-Paw/typeface-minecraft, which is included at assets/minecraft.woff2 in the package. You then add an @font-face rule to your CSS declaring the font family and pointing at the woff2 file.

### How is skinview3d packaged and built?

The npm package is skinview3d, version 3.4.2, with the entry point libs/skinview3d.js and the files libs, bundles and assets. The build runs tsc, rollup and vite in parallel through npm run build, with a separate typedoc step for the API documentation, and the licence is MIT.

## Sources

- [bs-community/skinview3d on GitHub](https://github.com/bs-community/skinview3d)
- [Issues](https://github.com/bs-community/skinview3d/issues)
- [License: MIT](https://github.com/bs-community/skinview3d/blob/master/LICENSE)
- [Project website](https://skinview3d-demo.vercel.app)
- [README](https://github.com/bs-community/skinview3d/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bs-community-skinview3d
