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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@teammeer.com/3dtostrip

v0.1.0

Published

Transform heavy 3D models into lightweight, hover rotatable 360° previews without WebGL. Converts any 3D model (GLB, GLTF, OBJ, FBX) into a lightweight, interactive sprite strip with 18 frames showing a smooth 360° rotation. Perfect for galleries, e-comme

Readme

npm version license PRs welcome Made by Team Meer

3D to Sprite Strip

Transform heavy 3D models into lightweight, hover rotatable 360° previews without WebGL.

Problem: Full 3D rendering is great but often overkill, especially when you just need a quick preview. Traditional 3D models are heavy, slow to load, and require complex WebGL setup.

Solution: This package converts any 3D model (GLB, GLTF, OBJ, FBX) into a lightweight, interactive sprite strip with 18 frames showing a smooth 360° rotation. It is not a replacement for full 3D rendering but a perfect optimization for previews, galleries, and e-commerce where you only need a fast, realistic 3D impression without the performance cost.

npm install @teammeer.com/3dtostrip
npm install react react-dom three

Quick Start

import { useSpriteStripGenerator, SpriteStripViewer } from '@teammeer.com/3dtostrip';

function App() {
  const { generateSpriteStrip, generatedData } = useSpriteStripGenerator();

  return (
    <>
      <input
        type="file"
        accept=".glb,.gltf,.obj"
        onChange={(e) => generateSpriteStrip(e.target.files[0])}
      />

      {generatedData && (
        <SpriteStripViewer
          spriteStripUrl={generatedData.spriteStripUrl}
        />
      )}
    </>
  );
}

Done. Upload → Generate → Interactive 3D preview.

Features

  • 18 frames of your 3D model rotating 360°
  • Hover to rotate - move mouse left/right
  • Works everywhere - no WebGL needed
  • Tiny file size - just one image
  • Custom textures - Apply textures to your models (advanced)

The Gallery Problem → Solved

| Before (3D WebGL) | After (Sprite Strips) | |----------------------|---------------------------| | Slow loading - Each model loads heavy WebGL scenes | Instant loading - Just images, no WebGL needed | | Memory bloat - Browser memory gets exhausted | Lightweight - Load 100+ models instantly | | Mobile struggles - Complex 3D rendering fails | Works everywhere - Any device, any browser | | Complex setup - WebGL, shaders, lighting setup | Zero setup - Just upload and display | | Expensive - Heavy server processing | Cheap - Static images, CDN-friendly | | Limited - Only modern browsers | Universal - Works on IE11+ |

// Gallery with 50+ models - all load instantly
{models.map(model => (
  <SpriteStripViewer
    key={model.id}
    spriteStripUrl={model.spriteStrip}
  />
))}

Auto-Save to Database

const { generateSpriteStrip } = useSpriteStripGenerator({
  onSubmit: async (data) => {
    await fetch('/api/save', {
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
});

Flow: Upload → Generate → Auto-save → Done.

Supported Formats

  • GLB, GLTF (recommended)
  • OBJ, FBX
  • Max file size: 50MB

Next.js Example

'use client'; // Required!

import { useSpriteStripGenerator, SpriteStripViewer } from '@teammeer.com/3dtostrip';

export default function Page() {
  const { generateSpriteStrip, generatedData, isGenerating, progress, error } =
    useSpriteStripGenerator();

  return (
    <div>
      <input
        type="file"
        accept=".glb,.gltf,.obj"
        onChange={(e) => generateSpriteStrip(e.target.files[0])}
        disabled={isGenerating}
      />

      {isGenerating && <p>Generating... {Math.round(progress)}%</p>}
      {error && <p>Error: {error.message}</p>}

      {generatedData && (
        <SpriteStripViewer spriteStripUrl={generatedData.spriteStripUrl} />
      )}
    </div>
  );
}

Custom Lighting Controls

You can create custom slider components to control lighting in real-time. Here's how to implement interactive lighting controls:

Basic Lighting Controls Example

import React, { useState } from 'react';
import { useSpriteStripGenerator } from '@teammeer.com/3dtostrip';

const LightingControlsExample = () => {
  const [lightingConfig, setLightingConfig] = useState({
    ambientLight: { intensity: 0.6 },
    directionalLight: { intensity: 1.0, position: { x: 2, y: 2, z: 2 } }
  });

  const { generateSpriteStrip, generatedData, isGenerating } = useSpriteStripGenerator({
    lighting: lightingConfig,
    backgroundColor: '#f0f0f0',
    ratio: '16:9'
  });

  return (
    <div>
      {/* File Upload */}
      <input
        type="file"
        accept=".glb,.gltf,.obj,.fbx"
        onChange={(e) => generateSpriteStrip(e.target.files?.[0])}
        disabled={isGenerating}
      />

      {/* Lighting Controls */}
      <div>
        <label>Ambient Light: {lightingConfig.ambientLight.intensity}</label>
        <input
          type="range"
          min="0"
          max="2"
          step="0.1"
          value={lightingConfig.ambientLight.intensity}
          onChange={(e) => setLightingConfig(prev => ({
            ...prev,
            ambientLight: { intensity: parseFloat(e.target.value) }
          }))}
        />
      </div>

      <div>
        <label>Directional Light: {lightingConfig.directionalLight.intensity}</label>
        <input
          type="range"
          min="0"
          max="3"
          step="0.1"
          value={lightingConfig.directionalLight.intensity}
          onChange={(e) => setLightingConfig(prev => ({
            ...prev,
            directionalLight: { ...prev.directionalLight, intensity: parseFloat(e.target.value) }
          }))}
        />
      </div>

      {/* Position Controls */}
      <div>
        <label>X Position: {lightingConfig.directionalLight.position.x}</label>
        <input
          type="range"
          min="-5"
          max="5"
          step="0.1"
          value={lightingConfig.directionalLight.position.x}
          onChange={(e) => setLightingConfig(prev => ({
            ...prev,
            directionalLight: {
              ...prev.directionalLight,
              position: { ...prev.directionalLight.position!, x: parseFloat(e.target.value) }
            }
          }))}
        />
      </div>

      {generatedData && (
        <img src={generatedData.spriteStripUrl} alt="Generated Sprite Strip" />
      )}
    </div>
  );
};

Lighting Control Ranges

| Control | Range | Step | Description | |---------|-------|------|-------------| | Ambient Intensity | 0 - 2 | 0.1 | Overall scene brightness | | Directional Intensity | 0 - 3 | 0.1 | Main light strength | | X Position | -5 to 5 | 0.1 | Left/Right light position | | Y Position | -5 to 5 | 0.1 | Up/Down light position | | Z Position | -5 to 5 | 0.1 | Forward/Back light position |

API Reference

useSpriteStripGenerator(options)

| Option | Type | Default | Description | |--------|------|---------|-------------| | onGenerate | (data) => void | - | Called when generation completes | | onSubmit | (data) => Promise<void> | - | Auto-save to database | | ratio | '16:9' \| '4:3' \| '1:1' \| '3:2' \| '5:4' \| 'auto' \| 'custom' | '16:9' | Output aspect ratio | | customRatio | { width: number; height: number } | - | Custom dimensions (when ratio='custom') | | backgroundColor | string | '#ffffff' | Render background color | | backgroundOpacity | number | 1.0 | Background transparency (0-1) | | maxTextures | number | 10 | Max texture uploads | | acceptedFormats | string[] | ['.png', '.jpg', '.jpeg', '.webp'] | Allowed texture formats | | resolution | ResolutionSettings | - | Resolution and quality controls | | lighting | LightingControls | - | Advanced lighting configuration |

ResolutionSettings

| Option | Type | Default | Description | |--------|------|---------|-------------| | width | number | - | Custom width (overrides ratio) | | height | number | - | Custom height (overrides ratio) | | quality | 'low' \| 'medium' \| 'high' \| 'ultra' | 'high' | Output quality preset | | compression | number | 0 | File compression (0-1, higher = smaller file) |

LightingControls

| Option | Type | Default | Description | |--------|------|---------|-------------| | ambientLight | { intensity?: number } | { intensity: 0.6 } | Ambient lighting intensity (0-2) | | directionalLight | { intensity?: number; position?: { x: number; y: number; z: number } } | { intensity: 1.0, position: { x: 2, y: 2, z: 2 } } | Main directional light intensity and position |

Returns:

  • generateSpriteStrip(file) - Generate from 3D model
  • isGenerating - Loading state
  • progress - Generation progress (0-100)
  • generatedData - Result data
  • error - Error state
  • getPresetLighting(preset) - Get lighting presets
  • getPresetResolution(preset) - Get resolution presets

Helper Functions

| Function | Parameters | Returns | Description | |----------|------------|---------|-------------| | getPresetLighting | 'studio' \| 'outdoor' \| 'dramatic' \| 'soft' | LightingControls | Pre-configured lighting setups | | getPresetResolution | 'mobile' \| 'desktop' \| '4k' \| 'print' | ResolutionSettings | Pre-configured resolution settings |

SpriteStripViewer

| Prop | Type | Default | Description | |------|------|---------|-------------| | spriteStripUrl | string | required | Sprite strip image URL | | hover | boolean | true | Enable hover rotation | | ratio | '16:9' \| '4:3' \| '1:1' \| 'auto' | '16:9' | Display aspect ratio | | className | string | '' | CSS class name | | onFrameChange | (frame: number) => void | - | Frame change callback |

Supported File Types

3D Models: .glb, .gltf, .obj, .fbx Textures: .png, .jpg, .jpeg, .webp Smart Limits: 50MB per file (prevents browser crashes, ensures fast processing)


Perfect for:

  • Product galleries - Show 100+ items without lag
  • E-commerce - Fast product previews
  • Portfolios - Interactive 3D artwork
  • Real estate - Property 360° views
  • Documentation - Assembly instructions

The magic: Transform heavy 3D galleries into lightning-fast image galleries with full interactivity.


🤝 Contributing

We welcome contributions! This project is open source and community-driven.

How to Contribute

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

git clone https://github.com/teammeer/3dtostrip.git
cd 3dtostrip
npm install
npm run build

Ideas & Feedback

  • 💡 Have an idea? Open an issue with the enhancement label
  • 🐛 Found a bug? Open an issue with the bug label
  • 💬 Questions? Start a discussion
  • Like this project? Give it a star!

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

👥 Authors

🙏 Acknowledgments

  • Built with Three.js for 3D model processing
  • Powered by React for component architecture

Made with ❤️ by the teammeer