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

png-to-vtf

v0.3.0

Published

Convert PNG images to VTF (Valve Texture Format) for Source engine games like Garry's Mod, Counter-Strike, Team Fortress 2, and Half-Life 2. Works in both Node.js and browser environments.

Readme

png-to-vtf

Convert PNG images to VTF (Valve Texture Format) for Source engine games like Garry's Mod, Counter-Strike, Team Fortress 2, and Half-Life 2. Works in both Node.js and browser environments.

[!WARNING] This library was for 95% generated by an AI (Claude Opus 4.5) based on the VTF specification. While it has been tested and works for basic use cases, it should not be considered production-ready or fully compliant with all VTF features. Use at your own risk.

Features

  • 🎮 Convert PNG images to VTF format for Source engine games
  • 🌐 Works in both Node.js and browser environments
  • 🖼️ Comprehensive format support (DXT5, RGBA8888, BGRA8888, BGR888, I8, IA88, and more)
  • 📐 Automatic mipmap generation
  • 🔄 Resize images during conversion (with optional power-of-2 clamping)
  • 💡 Automatic reflectivity calculation for VRAD
  • 🚩 Full VTF flags support
  • 💾 File, buffer, and Canvas-based APIs
  • ⚡ Fast conversion using sharp (Node.js) or Canvas API (browser)

Installation

npm install png-to-vtf

For browser usage via CDN:

<!-- IIFE bundle (exposes window.PNGToVTF) -->
<script src="https://unpkg.com/png-to-vtf/dist/png-to-vtf.browser.min.js"></script>

<!-- Or as ES module -->
<script type="module">
  import { CanvasToVTF, VTF_FORMATS } from 'https://unpkg.com/png-to-vtf/dist/png-to-vtf.browser.min.mjs';
</script>

Quick Start

Node.js (ES Modules)

import { convertPNGToVTF, VTF_FORMATS } from 'png-to-vtf';

// Simple conversion
await convertPNGToVTF('input.png', 'output.vtf');

// With options
await convertPNGToVTF('input.png', 'output.vtf', {
    format: VTF_FORMATS.BGRA8888,
    width: 512,
    height: 512,
    clampToPowerOf2: true  // Auto-resize to power of 2
});

Node.js (CommonJS)

const { convertPNGToVTF, VTF_FORMATS } = require('png-to-vtf');

(async () => {
    // Simple conversion
    await convertPNGToVTF('input.png', 'output.vtf');

    // With options
    await convertPNGToVTF('input.png', 'output.vtf', {
        format: VTF_FORMATS.BGRA8888,
        width: 512,
        height: 512,
        clampToPowerOf2: true
    });
})();

Browser

// Using the global PNGToVTF object
const { CanvasToVTF, VTF_FORMATS, canvasToVTF } = window.PNGToVTF;

// Quick conversion and download
canvasToVTF(myCanvas, 'spray', VTF_FORMATS.RGBA8888);

// Or use the class for more control
const converter = new CanvasToVTF({
    format: VTF_FORMATS.BGRA8888,
    mipmaps: true
});
converter.downloadFromCanvas(myCanvas, 'texture');

API Reference

Node.js Functions

convertPNGToVTF(inputPath, outputPath, options?)

Converts a PNG file to VTF format and saves it to disk. Node.js only - requires sharp.

Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | inputPath | string | Path to the input PNG file | | outputPath | string | Path for the output VTF file | | options | object | Optional conversion settings | | options.format | number | VTF format (default: VTF_FORMATS.RGBA8888) | | options.width | number | Target width (should be power of 2) | | options.height | number | Target height (should be power of 2) | | options.generateMips | boolean | Generate mipmaps (default: true) | | options.flags | number | VTF flags (auto-detected if not specified) | | options.clampToPowerOf2 | boolean | Auto-resize to nearest power of 2 (default: false) |

Returns: Promise<{ width, height, format }>

Example:

import { convertPNGToVTF, VTF_FORMATS, VTF_FLAGS } from 'png-to-vtf';

const result = await convertPNGToVTF('texture.png', 'texture.vtf', {
    format: VTF_FORMATS.BGR888,
    clampToPowerOf2: true,
    flags: VTF_FLAGS.TRILINEAR | VTF_FLAGS.ANISOTROPIC
});

console.log(`Created ${result.width}x${result.height} VTF`);

convertPNGBufferToVTF(pngBuffer, options?)

Converts a PNG buffer to VTF format and returns the VTF data as a buffer. Node.js only - requires sharp.

Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | pngBuffer | Buffer | PNG image data as a buffer | | options | object | Optional conversion settings (same as convertPNGToVTF) |

Returns: Promise<Buffer> - VTF file data

Example:

import fs from 'fs';
import { convertPNGBufferToVTF, VTF_FORMATS } from 'png-to-vtf';

const pngData = fs.readFileSync('texture.png');
const vtfData = await convertPNGBufferToVTF(pngData, {
    format: VTF_FORMATS.BGRA8888,
    clampToPowerOf2: true
});

fs.writeFileSync('texture.vtf', vtfData);

convertRGBAToVTF(rgbaData, width, height, format?, generateMipsOrOptions?)

Converts raw RGBA pixel data to VTF format.

Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | rgbaData | Buffer | - | Raw RGBA pixel data (4 bytes per pixel) | | width | number | - | Image width | | height | number | - | Image height | | format | number | RGBA8888 | Target VTF format | | generateMipsOrOptions | boolean or object | true | Generate mipmaps, or options object |

Options object properties:

| Property | Type | Default | Description | |----------|------|---------|-------------| | generateMips | boolean | true | Generate mipmaps | | flags | number | null | VTF flags (auto-detected if null) | | calculateReflectivityValue | boolean | true | Calculate reflectivity for VRAD | | frames | number | 1 | Number of animation frames | | bumpmapScale | number | 1.0 | Bumpmap scale value |

Returns: Buffer - VTF file data

Example:

import { convertRGBAToVTF, VTF_FORMATS } from 'png-to-vtf';

// Create a 2x2 red texture
const rgbaData = Buffer.from([
    255, 0, 0, 255,  // Red pixel
    255, 0, 0, 255,  // Red pixel
    255, 0, 0, 255,  // Red pixel
    255, 0, 0, 255   // Red pixel
]);

// Simple usage (legacy)
const vtfData = convertRGBAToVTF(rgbaData, 2, 2, VTF_FORMATS.RGBA8888, false);

// With options object
const vtfData2 = convertRGBAToVTF(rgbaData, 2, 2, VTF_FORMATS.BGRA8888, {
    generateMips: true,
    calculateReflectivityValue: true
});

Browser Functions

These functions are designed for browser environments but also work in Node.js.

convertImageDataToVTF(imageData, options?)

Converts ImageData (from Canvas) to VTF format. Works in both Node.js and browser.

Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | imageData | ImageData | ImageData object from canvas getImageData() | | options.format | number | VTF format (default: VTF_FORMATS.RGBA8888) | | options.generateMips | boolean | Generate mipmaps (default: true) | | options.flags | number | VTF flags (auto-detected if not specified) |

Returns: Uint8Array - VTF file data

Example:

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

const vtfData = convertImageDataToVTF(imageData, {
    format: VTF_FORMATS.BGRA8888,
    generateMips: true
});

CanvasToVTF Class

A convenient class for converting HTML5 Canvas to VTF format in the browser.

Constructor:

new CanvasToVTF(options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | format | number | RGBA8888 | VTF format | | mipmaps | boolean | true | Generate mipmaps | | flags | number | auto | VTF flags |

Methods:

  • convertCanvas(canvas) - Convert canvas to VTF, returns Uint8Array
  • convertImageData(imageData) - Convert ImageData to VTF, returns Uint8Array
  • downloadFromCanvas(canvas, filename?) - Convert and trigger download (browser only)

Example:

const { CanvasToVTF, VTF_FORMATS } = window.PNGToVTF;

const converter = new CanvasToVTF({
    format: VTF_FORMATS.DXT5,
    mipmaps: true
});

// Get VTF data
const vtfData = converter.convertCanvas(myCanvas);

// Or directly download
converter.downloadFromCanvas(myCanvas, 'my-spray');

canvasToVTF(canvas, filename?, format?, options?)

Quick convert and download canvas to VTF. Browser only.

Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | canvas | HTMLCanvasElement | - | Source canvas | | filename | string | 'texture' | Output filename (without .vtf) | | format | number | RGBA8888 | VTF format | | options.mipmaps | boolean | true | Generate mipmaps |

Example:

// Quick one-liner to download
canvasToVTF(myCanvas, 'spray', VTF_FORMATS.RGBA8888);

downloadVTF(vtfData, filename?)

Download VTF data as a file. Browser only.

Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | vtfData | Uint8Array | - | VTF file data | | filename | string | 'texture' | Filename (without .vtf) |


createVTFHeader(width, height, format, options?)

Creates a VTF 7.2 header.

Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | width | number | - | Image width | | height | number | - | Image height | | format | number | - | VTF format constant | | options | object | {} | Header options |

Options:

| Property | Type | Default | Description | |----------|------|---------|-------------| | mipmaps | boolean | false | Whether mipmaps are included | | flags | number | null | VTF flags (auto-detected if null) | | frames | number | 1 | Number of animation frames | | firstFrame | number | 0 | First frame index | | reflectivity | [r, g, b] | [0, 0, 0] | Reflectivity values (0-1) | | bumpmapScale | number | 1.0 | Bumpmap scale | | depth | number | 1 | Texture depth (for volumetric) |

Returns: Buffer - 80-byte VTF header


VTF_FORMATS

Available VTF image formats:

| Format | Value | Bits | Description | |--------|-------|------|-------------| | RGBA8888 | 0 | 32 | RGBA (8 bits per channel) | | ABGR8888 | 1 | 32 | ABGR (8 bits per channel) | | RGB888 | 2 | 24 | RGB (no alpha) | | BGR888 | 3 | 24 | BGR - preferred for opaque textures | | RGB565 | 4 | 16 | RGB (5-6-5 bits) | | I8 | 5 | 8 | Grayscale (luminance only) | | IA88 | 6 | 16 | Grayscale + Alpha | | A8 | 8 | 8 | Alpha only | | ARGB8888 | 11 | 32 | ARGB (8 bits per channel) | | BGRA8888 | 12 | 32 | BGRA - common format with alpha | | DXT1 | 13 | 4 | Block compression - recommended for opaque | | DXT3 | 14 | 8 | Block compression, sharp alpha | | DXT5 | 15 | 8 | Block compression, smooth alpha - recommended for alpha | | BGRX8888 | 16 | 32 | BGR with unused alpha byte | | BGR565 | 17 | 16 | BGR (5-6-5) - preferred over RGB565 | | BGRA4444 | 19 | 16 | BGRA (4 bits per channel) | | BGRA5551 | 21 | 16 | BGRA with 1-bit alpha | | UV88 | 22 | 16 | For bump/normal maps |


VTF_FLAGS

Texture flags that control rendering behavior:

| Flag | Value | Description | |------|-------|-------------| | POINTSAMPLE | 0x0001 | Point sampling (no filtering) | | TRILINEAR | 0x0002 | Trilinear filtering | | CLAMPS | 0x0004 | Clamp S texture coordinate | | CLAMPT | 0x0008 | Clamp T texture coordinate | | ANISOTROPIC | 0x0010 | Anisotropic filtering | | HINT_DXT5 | 0x0020 | Hint to use DXT5 compression | | NORMAL | 0x0080 | Normal map | | NOMIP | 0x0100 | No mipmaps | | NOLOD | 0x0200 | No level of detail | | ALL_MIPS | 0x0400 | Load all mipmap levels | | ONEBITALPHA | 0x1000 | 1-bit alpha (on/off) | | EIGHTBITALPHA | 0x2000 | 8-bit alpha channel | | ENVMAP | 0x4000 | Environment map | | RENDERTARGET | 0x8000 | Render target | | SSBUMP | 0x8000000 | Self-shadowed bump map |

Example:

import { VTF_FLAGS } from 'png-to-vtf';

// Combine flags with bitwise OR
const flags = VTF_FLAGS.TRILINEAR | VTF_FLAGS.ANISOTROPIC | VTF_FLAGS.EIGHTBITALPHA;

Utility Functions

import {
    isPowerOf2,
    nextPowerOf2,
    getBytesPerPixel,
    calculateMipmapCount,
    convertToFormat,
    generateMipmaps,
    VTF_FORMATS
} from 'png-to-vtf';

// Check if dimensions are valid
isPowerOf2(256);  // true
isPowerOf2(300);  // false

// Get nearest power of 2
nextPowerOf2(300);  // 512

// Get bytes per pixel for a format
getBytesPerPixel(VTF_FORMATS.RGBA8888);  // 4
getBytesPerPixel(VTF_FORMATS.BGR888);    // 3
getBytesPerPixel(VTF_FORMATS.I8);        // 1

// Calculate mipmap levels
calculateMipmapCount(256, 256);  // 9 (256, 128, 64, 32, 16, 8, 4, 2, 1)

// Convert RGBA to another format
const bgraData = convertToFormat(rgbaBuffer, VTF_FORMATS.BGRA8888);

// Generate mipmap chain (returns smallest-first for VTF)
const mipmaps = generateMipmaps(rgbaBuffer, 256, 256);

Image Size Requirements

For best compatibility with Source engine games, texture dimensions should be powers of 2:

  • 16, 32, 64, 128, 256, 512, 1024, 2048, 4096

Use the width and height options to resize images, or set clampToPowerOf2: true to automatically resize to the nearest power of 2.

import { convertPNGToVTF } from 'png-to-vtf';

// Auto-resize any image to valid dimensions
await convertPNGToVTF('any-size.png', 'output.vtf', {
    clampToPowerOf2: true
});

VTF Version

This library generates VTF version 7.2 files with 80-byte headers. This format is compatible with:

  • VTFEdit
  • Garry's Mod
  • Counter-Strike: Source
  • Team Fortress 2
  • Half-Life 2 and episodes
  • Portal / Portal 2
  • Left 4 Dead / Left 4 Dead 2
  • And other Source engine games

Mipmaps

By default, mipmaps are automatically generated. Mipmaps are smaller versions of your texture used when viewing from a distance, improving both performance and visual quality.

To disable mipmap generation:

import { convertPNGToVTF } from 'png-to-vtf';

await convertPNGToVTF('input.png', 'output.vtf', {
    generateMips: false
});

Reflectivity

The library automatically calculates the average reflectivity of your texture. This value is used by VRAD (Valve's radiosity compiler) for lighting calculations. The reflectivity is stored in the VTF header.

Format Recommendations

| Use Case | Recommended Format | |----------|-------------------| | Opaque textures | BGR888 or BGRA8888 | | Textures with alpha | BGRA8888 | | Grayscale (masks, halos) | I8 | | Grayscale with alpha (smoke) | IA88 | | Lower quality/smaller size | BGR565 or BGRA4444 | | Normal maps | BGR888 or UV88 | | Compressed opaque | DXT1 | | Compressed with alpha | DXT5 |

Limitations

  • Animated textures (multi-frame VTF) are not fully supported via API
  • Cubemaps and volume textures are not supported
  • No low-resolution thumbnail generation
  • Browser: Image resizing requires manual use of Canvas API (use resizeImageBrowser() helper)
  • Browser: File I/O not available (use downloadVTF() for downloads)

Browser vs Node.js Feature Comparison

| Feature | Node.js | Browser | |---------|---------|---------| | Convert PNG file | ✅ convertPNGToVTF | ❌ | | Convert PNG buffer | ✅ convertPNGBufferToVTF | ❌ | | Convert RGBA data | ✅ convertRGBAToVTF | ✅ convertRGBAToVTF | | Convert ImageData | ✅ convertImageDataToVTF | ✅ convertImageDataToVTF | | Convert Canvas | ✅ CanvasToVTF | ✅ CanvasToVTF | | Auto-resize | ✅ Sharp | ⚠️ Canvas API | | DXT Compression | ✅ | ✅ | | Download VTF | ❌ | ✅ downloadVTF |