docs.rs failed to build terminal-pixel-animation-0.3.5
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
terminal-pixel-animation
Render pixel images as Unicode characters in the terminal with True Color support. Also available as a WebAssembly module for browser use with React hooks.
RGBピクセル画像をUnicode文字に変換し、ターミナルやブラウザにTrue Colorで表示するライブラリ。React Hooks対応。
Features
| Renderer | Language | Cells per pixel | Resolution | Best for |
|---|---|---|---|---|
| Braille | Odin | 2x4 (8 px/cell) | High | Detailed still images, thumbnails |
| Half-block | Zig | 1x2 (2 px/cell) | Medium | High frame-rate video playback |
- Aspect-ratio-preserving resize with letterboxing
- ANSI True Color (24-bit) output helpers
- Luminance-weighted color averaging with saturation boost (Braille mode)
- WebAssembly target for browser use via
wasm-pack/wasm-bindgen - React Hooks (
WasmProvider,useBraille,useHalfBlock)
アスペクト比を保ったリサイズ、ANSI True Color出力、WebAssembly対応、React Hooks対応。
Installation
Rust (crates.io)
[]
= "0.2"
npm
# Core WASM module / WASMコアモジュール
# React hooks (optional) / React Hooks(オプション)
Build dependencies: Requires
odin,zig,objcopy, andwasm-packin yourPATH.ビルド時の依存関係: Rust, Odin, Zig, objcopy (binutils), wasm-pack が必要です。
Quick Start
Native (Terminal)
use ;
// RGB8 pixel buffer (width * height * 3 bytes)
let pixels: = load_your_image;
let = ;
// Render into Braille cells (80 columns x 30 rows)
let cells = render_braille.unwrap;
// Print to terminal
print_braille_to_terminal;
WASM (Browser)
import init from "terminal-pixel-animation";
await ;
// RGB8 pixel buffer as Uint8Array
const cells = ;
// Decode Braille cells
React
import { useState, useEffect } from "react";
import { WasmProvider, useBraille } from "terminal-pixel-animation-react";
function App() {
return (
<WasmProvider>
<PixelCanvas />
</WasmProvider>
);
}
function PixelCanvas() {
const [pixels, setPixels] = useState<Uint8Array | null>(null);
const { decoded, loading } = useBraille(pixels, 320, 240, 80, 30);
if (loading) return <p>Loading WASM...</p>;
if (!decoded) return null;
return (
<pre style={{ fontFamily: "monospace", lineHeight: "1", fontSize: "10px" }}>
{decoded.map((cell, i) => (
<span key={i} style={{ color: `rgb(${cell.r},${cell.g},${cell.b})` }}>
{cell.char}
</span>
))}
</pre>
);
}
WASM Guide
Setup
Browser Usage
import init from "terminal-pixel-animation";
// Initialize WASM (call once)
await ;
// Get RGB buffer from webcam
const canvas = document.;
const ctx = canvas.;
canvas. = video.;
canvas. = video.;
ctx.;
const imageData = ctx.;
// Convert RGBA to RGB
const rgb = ;
// Braille rendering
const cells = ;
// Draw on canvas
const displayCanvas = document.;
const displayCtx = displayCanvas.;
displayCanvas. = 80 * 8;
displayCanvas. = 30 * 14;
Half-block Rendering
const cells = ;
// Cell is 6 bytes: [R_fg, G_fg, B_fg, R_bg, G_bg, B_bg]
React Guide
Setup
WasmProvider
Wrap your app with WasmProvider at the root. The WASM module is loaded once.
import { WasmProvider } from "terminal-pixel-animation-react";
function App() {
return (
<WasmProvider>
<MyComponent />
</WasmProvider>
);
}
useBraille Hook
import { useBraille } from "terminal-pixel-animation-react";
function MyComponent() {
const { cells, decoded, loading, error } = useBraille(pixels, 320, 240, 80, 30);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!decoded) return null;
// decoded: [{ char, r, g, b }, ...] — flat array, row-major order
return (
<pre style={{ fontFamily: "monospace", lineHeight: "1", fontSize: "10px" }}>
{decoded.map((cell, i) => (
<span key={i} style={{ color: `rgb(${cell.r},${cell.g},${cell.b})` }}>
{cell.char}
</span>
))}
</pre>
);
}
useHalfBlock Hook
import { useHalfBlock } from "terminal-pixel-animation-react";
function MyComponent() {
const { decoded } = useHalfBlock(pixels, 320, 240, 80, 30);
// decoded: [{ rFg, gFg, bFg, rBg, gBg, bBg }, ...]
return (
<div>
{decoded?.map((cell, i) => (
<div
key={i}
style={{
display: "inline-block",
width: 8,
height: 14,
background: `linear-gradient(to bottom, rgb(${cell.rFg},${cell.gFg},${cell.bFg}) 50%, rgb(${cell.rBg},${cell.gBg},${cell.bBg}) 50%)`,
}}
/>
))}
</div>
);
}
Real-time Webcam Rendering
import { useState, useEffect, useRef } from "react";
import { WasmProvider, useBraille } from "terminal-pixel-animation-react";
function App() {
return (
);
}
function WebcamDemo() {
const videoRef = useRef(null);
const [pixels, setPixels] = useState(null);
const [size, setSize] = useState({ w: 0, h: 0 });
useEffect(() => {
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
if (videoRef.current) {
videoRef.current.srcObject = stream;
videoRef.current.play();
}
});
}, []);
useEffect(() => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d")!;
const capture = () => {
const video = videoRef.current;
if (!video || video.readyState < 2) return;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const rgb = new Uint8Array(canvas.width * canvas.height * 3);
for (let i = 0, j = 0; i < imageData.data.length; i += 4, j += 3) {
rgb[j] = imageData.data[i];
rgb[j + 1] = imageData.data[i + 1];
rgb[j + 2] = imageData.data[i + 2];
}
setPixels(rgb);
setSize({ w: canvas.width, h: canvas.height });
};
const id = requestAnimationFrame(function loop() {
capture();
requestAnimationFrame(loop);
});
return () => cancelAnimationFrame(id);
}, []);
return (
);
}
function PixelDisplay({ pixels, width, height }: {
pixels: Uint8Array | null; width: number; height: number;
}) {
const canvasRef = useRef(null);
const { decoded } = useBraille(pixels, width, height, 100, 45);
useEffect(() => {
if (!decoded || !canvasRef.current) return;
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d")!;
canvas.width = 100 * 8;
canvas.height = 45 * 14;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = "14px monospace";
ctx.textBaseline = "top";
for (let i = 0; i < decoded.length; i++) {
const cell = decoded[i];
if (cell.char !== "\0" && cell.char !== " ") {
const col = i % 100;
const row = Math.floor(i / 100);
ctx.fillStyle = `rgb(${cell.r},${cell.g},${cell.b})`;
ctx.fillText(cell.char, col * 8, row * 14);
}
}
}, [decoded]);
return ;
}
API Reference
Rust (Native)
// Braille: 8 bytes per cell [utf8;4, R, G, B, _]
let cells = render_braille?;
// Half-block: 6 bytes per cell [R_fg, G_fg, B_fg, R_bg, G_bg, B_bg]
let cells = render_half_block?;
// Terminal output
print_braille_to_terminal;
print_halfblock_to_terminal;
WASM (JavaScript)
// Braille: Uint8Array, 8 bytes per cell
const cells = ;
// Half-block: Uint8Array, 6 bytes per cell
const cells = ;
React Hooks
| Hook | Arguments | Returns |
|---|---|---|
useBraille(pixels, w, h, cols, rows) |
Uint8Array | null, 4 numbers |
{ cells, decoded, loading, error } |
useHalfBlock(pixels, w, h, cols, rows) |
Uint8Array | null, 4 numbers |
{ cells, decoded, loading, error } |
Building from Source
Dependencies
- Rust (edition 2024)
- Odin compiler
- Zig compiler
objcopy(binutils)- wasm-pack (for WASM builds)
Native
WASM
React hooks
&& &&
Examples
Native
WASM
# Open http://localhost:8080/pkg/wasm-demo.html in your browser
Supports webcam live feed and video file playback.
Publishing
npm
&&
&&
crates.io
License
MIT