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.
Maintainers
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-vtfFor 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, returnsUint8ArrayconvertImageData(imageData)- Convert ImageData to VTF, returnsUint8ArraydownloadFromCanvas(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 |
