teamchong/pxpipePublic

cut Claude Code token usage by rendering text context as images

AI summary: A local proxy for Claude Code that reduces token costs by rendering bulky context into dense, compact images.

Stars
7K
+17 today
Forks
601
Watchers
25
Open issues
24
Open PRs
4
Contributors
~18
Commits
397
Branches
3

TypeScriptMITCreated May 20, 2026Last push 1d agoLatest release v0.11.1+131 stars this week+131 this month

Star history

since Jun 7, 2026
02K4K6KJun 2026Jun 2026Jul 2026Aug 2026
7K stars as of Aug 6, 2026, tracked back to Jun 7, 2026. Historical curve reconstructed from public GitHub event archives, calibrated to the current total.

Contribution activity

commits per day, last 52 weeks
AugSepOctNovDecJanFebMarAprMayJunJulMonWedFri2025-08-03: 0 commits2025-08-04: 0 commits2025-08-05: 0 commits2025-08-06: 0 commits2025-08-07: 0 commits2025-08-08: 0 commits2025-08-09: 0 commits2025-08-10: 0 commits2025-08-11: 0 commits2025-08-12: 0 commits2025-08-13: 0 commits2025-08-14: 0 commits2025-08-15: 0 commits2025-08-16: 0 commits2025-08-17: 0 commits2025-08-18: 0 commits2025-08-19: 0 commits2025-08-20: 0 commits2025-08-21: 0 commits2025-08-22: 0 commits2025-08-23: 0 commits2025-08-24: 0 commits2025-08-25: 0 commits2025-08-26: 0 commits2025-08-27: 0 commits2025-08-28: 0 commits2025-08-29: 0 commits2025-08-30: 0 commits2025-08-31: 0 commits2025-09-01: 0 commits2025-09-02: 0 commits2025-09-03: 0 commits2025-09-04: 0 commits2025-09-05: 0 commits2025-09-06: 0 commits2025-09-07: 0 commits2025-09-08: 0 commits2025-09-09: 0 commits2025-09-10: 0 commits2025-09-11: 0 commits2025-09-12: 0 commits2025-09-13: 0 commits2025-09-14: 0 commits2025-09-15: 0 commits2025-09-16: 0 commits2025-09-17: 0 commits2025-09-18: 0 commits2025-09-19: 0 commits2025-09-20: 0 commits2025-09-21: 0 commits2025-09-22: 0 commits2025-09-23: 0 commits2025-09-24: 0 commits2025-09-25: 0 commits2025-09-26: 0 commits2025-09-27: 0 commits2025-09-28: 0 commits2025-09-29: 0 commits2025-09-30: 0 commits2025-10-01: 0 commits2025-10-02: 0 commits2025-10-03: 0 commits2025-10-04: 0 commits2025-10-05: 0 commits2025-10-06: 0 commits2025-10-07: 0 commits2025-10-08: 0 commits2025-10-09: 0 commits2025-10-10: 0 commits2025-10-11: 0 commits2025-10-12: 0 commits2025-10-13: 0 commits2025-10-14: 0 commits2025-10-15: 0 commits2025-10-16: 0 commits2025-10-17: 0 commits2025-10-18: 0 commits2025-10-19: 0 commits2025-10-20: 0 commits2025-10-21: 0 commits2025-10-22: 0 commits2025-10-23: 0 commits2025-10-24: 0 commits2025-10-25: 0 commits2025-10-26: 0 commits2025-10-27: 0 commits2025-10-28: 0 commits2025-10-29: 0 commits2025-10-30: 0 commits2025-10-31: 0 commits2025-11-01: 0 commits2025-11-02: 0 commits2025-11-03: 0 commits2025-11-04: 0 commits2025-11-05: 0 commits2025-11-06: 0 commits2025-11-07: 0 commits2025-11-08: 0 commits2025-11-09: 0 commits2025-11-10: 0 commits2025-11-11: 0 commits2025-11-12: 0 commits2025-11-13: 0 commits2025-11-14: 0 commits2025-11-15: 0 commits2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 0 commits2026-01-28: 0 commits2026-01-29: 0 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 0 commits2026-02-03: 0 commits2026-02-04: 0 commits2026-02-05: 0 commits2026-02-06: 0 commits2026-02-07: 0 commits2026-02-08: 0 commits2026-02-09: 0 commits2026-02-10: 0 commits2026-02-11: 0 commits2026-02-12: 0 commits2026-02-13: 0 commits2026-02-14: 0 commits2026-02-15: 0 commits2026-02-16: 0 commits2026-02-17: 0 commits2026-02-18: 0 commits2026-02-19: 0 commits2026-02-20: 0 commits2026-02-21: 0 commits2026-02-22: 0 commits2026-02-23: 0 commits2026-02-24: 0 commits2026-02-25: 0 commits2026-02-26: 0 commits2026-02-27: 0 commits2026-02-28: 0 commits2026-03-01: 0 commits2026-03-02: 0 commits2026-03-03: 0 commits2026-03-04: 0 commits2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 0 commits2026-03-08: 0 commits2026-03-09: 0 commits2026-03-10: 0 commits2026-03-11: 0 commits2026-03-12: 0 commits2026-03-13: 0 commits2026-03-14: 0 commits2026-03-15: 0 commits2026-03-16: 0 commits2026-03-17: 0 commits2026-03-18: 0 commits2026-03-19: 0 commits2026-03-20: 0 commits2026-03-21: 0 commits2026-03-22: 0 commits2026-03-23: 0 commits2026-03-24: 0 commits2026-03-25: 0 commits2026-03-26: 0 commits2026-03-27: 0 commits2026-03-28: 0 commits2026-03-29: 0 commits2026-03-30: 0 commits2026-03-31: 0 commits2026-04-01: 0 commits2026-04-02: 0 commits2026-04-03: 0 commits2026-04-04: 0 commits2026-04-05: 0 commits2026-04-06: 0 commits2026-04-07: 0 commits2026-04-08: 0 commits2026-04-09: 0 commits2026-04-10: 0 commits2026-04-11: 0 commits2026-04-12: 0 commits2026-04-13: 0 commits2026-04-14: 0 commits2026-04-15: 0 commits2026-04-16: 0 commits2026-04-17: 0 commits2026-04-18: 0 commits2026-04-19: 0 commits2026-04-20: 0 commits2026-04-21: 0 commits2026-04-22: 0 commits2026-04-23: 0 commits2026-04-24: 0 commits2026-04-25: 0 commits2026-04-26: 0 commits2026-04-27: 0 commits2026-04-28: 0 commits2026-04-29: 0 commits2026-04-30: 0 commits2026-05-01: 0 commits2026-05-02: 0 commits2026-05-03: 0 commits2026-05-04: 0 commits2026-05-05: 0 commits2026-05-06: 0 commits2026-05-07: 0 commits2026-05-08: 0 commits2026-05-09: 0 commits2026-05-10: 0 commits2026-05-11: 0 commits2026-05-12: 0 commits2026-05-13: 0 commits2026-05-14: 0 commits2026-05-15: 0 commits2026-05-16: 0 commits2026-05-17: 0 commits2026-05-18: 35 commits2026-05-19: 30 commits2026-05-20: 25 commits2026-05-21: 18 commits2026-05-22: 9 commits2026-05-23: 7 commits2026-05-24: 8 commits2026-05-25: 4 commits2026-05-26: 4 commits2026-05-27: 1 commit2026-05-28: 0 commits2026-05-29: 3 commits2026-05-30: 0 commits2026-05-31: 3 commits2026-06-01: 0 commits2026-06-02: 0 commits2026-06-03: 0 commits2026-06-04: 0 commits2026-06-05: 1 commit2026-06-06: 0 commits2026-06-07: 0 commits2026-06-08: 0 commits2026-06-09: 20 commits2026-06-10: 12 commits2026-06-11: 9 commits2026-06-12: 5 commits2026-06-13: 0 commits2026-06-14: 2 commits2026-06-15: 1 commit2026-06-16: 3 commits2026-06-17: 12 commits2026-06-18: 3 commits2026-06-19: 13 commits2026-06-20: 1 commit2026-06-21: 4 commits2026-06-22: 5 commits2026-06-23: 4 commits2026-06-24: 0 commits2026-06-25: 9 commits2026-06-26: 4 commits2026-06-27: 4 commits2026-06-28: 0 commits2026-06-29: 1 commit2026-06-30: 3 commits2026-07-01: 12 commits2026-07-02: 7 commits2026-07-03: 16 commits2026-07-04: 13 commits2026-07-05: 2 commits2026-07-06: 0 commits2026-07-07: 0 commits2026-07-08: 1 commit2026-07-09: 6 commits2026-07-10: 3 commits2026-07-11: 7 commits2026-07-12: 4 commits2026-07-13: 2 commits2026-07-14: 2 commits2026-07-15: 1 commit2026-07-16: 11 commits2026-07-17: 7 commits2026-07-18: 8 commits2026-07-19: 4 commits2026-07-20: 2 commits2026-07-21: 4 commits2026-07-22: 8 commits2026-07-23: 0 commits2026-07-24: 0 commits2026-07-25: 3 commits2026-07-26: 4 commits2026-07-27: 4 commits2026-07-28: 3 commits2026-07-29: 0 commits2026-07-30: 0 commits2026-07-31: 0 commits2026-08-01: 0 commits
397 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Breakout launch

    6,966 stars in 79 days

  • Actively maintained

    Pushed within 48 hours

  • Permissive license

    MIT

  • Continuous integration

    Automated checks passing

  • Repeat trending

    3 trending appearances

What pxpipe does

Pxpipe sits between your terminal and the Claude API, acting as a local proxy that intercepts requests. Instead of sending thousands of text tokens for system prompts, history, or tool output, it renders this bulky context into images. Because Anthropic's vision channel bills image tokens by pixel dimensions rather than textual content, this approach compresses dense information like code and JSON at roughly 3.1 characters per token versus the standard 1 character per token. The AI agent, which already relies on screenshots for computer use, easily reads the content visually while slashing API costs.

Developers and power users running Claude Code or similar CLI agents who want to drastically cut down their Anthropic API costs. Requires basic proxy configuration and a CLI environment.

  • Visual context rendering: Converts standard text history and tool outputs into images before sending to the API.
  • Token cost reduction: Takes advantage of pixel-based pricing to pack more information per billed token.
  • Local proxy architecture: Runs silently in the background, intercepting and rewriting requests on the fly.
  • Seamless agent compatibility: Designed specifically for tools like Claude Code that natively support reading screenshot data.
  • High-density compression: Achieves over 3x the character density per token compared to plain text transmission.

Where teams use it

Reducing API bills

Essential for heavy users of Claude Code or similar agents who frequently process large codebases and long chat histories.

Extending context windows

Allows developers to include more tool output or logs in a single request by compressing it visually.

Agent optimization

Improves the economic viability of autonomous agents that rely heavily on reading extensive documentation or system prompts.

Bypassing text limits

Helps cram complex JSON or configuration files into queries that would otherwise exceed text token caps.

Getting started: Not directly specified, likely requires running the proxy binary and pointing Claude Code to its port.

README

main branch

pxpipe

Cut Claude Code's input tokens by rendering bulky context as images — the same system prompt, tool docs, and history, in a fraction of the tokens.

An image's token cost is fixed by its pixel dimensions, not by how much text is inside it. Dense content (code, JSON, tool output) packs ~3.1 chars per image-token vs ~1 char per text-token on real Claude Code traffic. The reader is the same vision channel that Anthropic's computer use already relies on for screenshots. pxpipe is a local proxy that uses that channel for context: it rewrites the bulky parts of each request into compact PNGs before it leaves your machine. At current Fable list prices that lands as a ~59–70% lower end-to-end bill — but prices move and workloads differ, so the durable number is the token cut itself, measured per-request against a free count_tokens counterfactual in ~/.pxpipe/events.jsonl.

This is what the model sees instead of text:

example: a real transformRequest output: system prompt + tool docs reflowed into one dense page, instruction banner on top, ↵ marking original newlines

~48k chars of system prompt + tool docs: ≈25k tokens as text, ≈2.7k image tokens as this page. Real pipeline output; the model reads renders like this at 100/100 (see benchmarks).

chart: characters a frontier context window holds, 2018–2026 — vendor text series including Grok 4.5; orange measured overlays are Fable 5 [1m] + pxpipe ~19.0M (4.8×) and Gemini 3.6 Flash + pxpipe ~21.3M (5.3×)

Eight years of context growth, in characters. Every text line tops out near ~4M chars (a 1M-token window at ~4 chars/token); Grok 4.5 is shown as a text-window point only (500K). The orange overlays are the same 1M windows read through pxpipe images — ~19.0M chars for Fable 5 (4.8×) and ~21.3M chars for Gemini 3.6 Flash (5.3× text capacity). Density is measured from a live render at generation time, not hand-typed: regenerate with npx tsx scripts/gen-context-chart.ts (source).

Demo

Fable 5 (the default, 100/100 reader) — plain left, pxpipe right:

Fable-AB-Demo.mp4

pxpipe counts an exact token 10/10 across 39 imaged filler files (matches grep line-for-line), gets the multi-step ledger arithmetic right, and ends the session at $6.06 with context to spare (73.5k/1M) vs $42.21 at 96% full. One caveat visible in the clip: the pxpipe arm needed a nudge to match the requested one-line output format.

Try it (30 seconds)

npx pxpipe-proxy                                  # proxy on 127.0.0.1:47821
ANTHROPIC_BASE_URL=http://127.0.0.1:47821 claude  # point Claude Code at it

Dashboard at http://127.0.0.1:47821/: tokens saved, every text→image conversion side by side, kill switch, live model chips. Responses stream normally — pxpipe compresses the request only, never the model's output. Recent turns stay text; the system prompt, tool docs, and older bulk history are imaged.

pxpipe warp

pxpipe warp -- claude          # also: cursor-agent, codex, or a shell alias

Same thing without ANTHROPIC_BASE_URL, so /remote-control, claude.ai connectors, and first-party gates keep working. Full instructions in the dashboard.

Offline export (no proxy)

You can render text, files, or diffs to PNG pages without running the proxy or connecting Claude Code:

npx pxpipe-proxy export src/
cat prompt.txt | npx pxpipe-proxy export --stdin
npx pxpipe-proxy export --git

If the package is installed, use pxpipe export instead of npx pxpipe-proxy export.

Each run writes a fresh pxpipe-export-XXXXXX/ output folder (the exact path is printed when the command finishes) containing page-*.png, factsheet.txt, manifest.json, and prompt.txt. Upload the PNG pages and paste the prompt into image-upload clients such as Cursor when you want dense visual context without running the proxy.

The honest part

  • It is lossy. Exact 12-char hex strings in dense imaged content: 13/15 on Fable 5 and 0/15 on Sol — misses are silent confabulations, not errors. Byte-exact values (IDs, hashes, secrets) must stay text; recent turns do. The factsheet selectively preserves up to 96 recognized precision-critical tokens, not every identifier. A dedicated verbatim-risk guard is not built yet.
  • Escape hatch: subagents on non-allowlisted models pass through as text — route byte-exact work there (CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-4-6, or model: sonnet in agent frontmatter).
  • Real work: SWE-bench Lite pilot 10/10 both arms at −65% request size; SWE-bench Pro 14/19 ON vs 15/19 OFF at −60%, verdicts agree 18/19, and the single split re-resolved 3/3 on replication — run-to-run variance, not compression. Small n; receipts in eval/.
  • Workload-dependent. Wins on token-dense content (~1 char/token), loses money on sparse prose (~3.5 chars/token); a profitability gate (calibrated on N=391 production rows) images only where the math wins.
  • Client-dependent. Savings track uncached bulk the client still re-sends as text. Claude Code re-sends system + tools + history on /anthropic/messages and typically lands ~60–70%. Details and measured splits: docs/CACHING_AND_SAVINGS.md.
Model support and rendering details
  • claude-opus-5: weaker recall than Fable 5 (verbatim 2/15 vs 13/15), good enough otherwise (100/100 arithmetic, 0/16 never-stated), ~4.7× context before /compact. Suggested effort: medium. Details: FINDINGS.md.
  • Model scope: default PXPIPE_MODELS=claude-fable-5,claude-opus-5,gemini-3.6-flash. Sol, GPT 5.5, and Grok are opt-in only (dashboard chips or PXPIPE_MODELS). The exact Sol id still matters. Sibling variants such as gpt-5.6-terra do not inherit Sol's allowlist or render profile. PXPIPE_MODELS=off disables imaging. Everything else passes through byte-identical. On the GPT path, tool definitions stay native JSON and no Anthropic cache_control markers are used. Responses history compression recognizes completed function_call/function_call_output pairs, including OpenCode's parallel calls-then-outputs rounds: only old closed rounds are imaged atomically; every open call and malformed/orphan state remains native. The base profile keeps the newest six completed pairs and allows 32 images; Sol keeps one pair and allows 64 images, while Grok allows 24 images. Opt-in long-session coverage can be changed (defensive cap 100) with PXPIPE_GPT_HISTORY_MAX_IMAGES=48 after validating the provider's request cap.
  • Per-model rendering: opt-in gpt-5.6-sol and Grok use native 14px JetBrains Mono glyphs in a 9×16 cell, 84 columns, and a 764px full-width strip; Claude keeps its 312-column, 1568×728 5×8 Spleen profile. These are selected by exact model id, including history pages and profitability math. Recognized IDs can ride in the bounded factsheet, and recent/open tool state stays native. Sol receipts and profile evidence.
  • Grok 4.5 (opt-in): native 14px / 84 cols / maxH 512 (100/100 arith, 97/98 gist). Off by default (dense hex still 0/15). Enable with PXPIPE_MODELS=claude-fable-5,grok-4.5 or the dashboard chip. eval/grok-density/QUALITY_RESULTS.md.

Benchmark results and receipts

Model quality

This matrix shows coverage as well as scores. means the model was not run on that test; it does not mean zero. Arithmetic uses novel random-number problems. Gist, state, and never-stated probes share one corpus. Never-stated is confabulations, so lower is better.

model arithmetic (N=100) gist (N=98) state (N=18) never-stated (N=16) dense hex (N=15) profile provenance and receipts
claude-fable-5 100/100 98/98 18/18 0/16 13/15 June 2026 production profiles: arithmetic + hex, gist/state/guards
google/gemini-3.6-flash 100/100 98/98 18/18 0/16 14/15 current shipped profile: quality results
claude-opus-5 100/100 94/98 17/18 0/16 2/15 current profile: arithmetic, gist/state/guards, dense hex
gpt-5.6-sol 98/100 83/98 17/18 4/16 0/15 prior 5×8 broad suite; native 14px pilot: 7/8 exact, 0 inventions, gist/guard pass: pilot
claude-opus-4-8 93/100 77/98 18/18 0/16 0/15 historical profile: arithmetic, gist/state/guards, dense hex
grok-4.5 100/100 97/98 17/18 0/16 0/15 native 14px/84 quality suite (live profile); quality, native-sweep
moonshotai/kimi-k3 79/100 84/98 15/18 1/16 0/15 generic GPT profile: quality results

Native-profile cost check

Offline export of the same deterministic 454,045-character dense record corpus through each complete profile produced:

model profile pages text estimate image tokens savings
Claude, Spleen 5×8 17 122,715 23,856 80.6%
Sol, JetBrains Mono 14px 45 122,715 65,424 46.7%

The text estimate uses 3.7 characters/token; image tokens use each model's provider formula and actual rendered page dimensions. These figures establish profile cost on this corpus, not a universal workload savings rate. Sol's paid fixtures estimated 42% while reading 7/8 exact with no unsupported inventions.

The runs use different transports and profile generations, not one identical image geometry. Fable and Opus use Claude; Gemini uses Google AI Studio; Sol and Grok use Codex Responses; Kimi K3 uses Cloudflare's OpenAI-compatible transport. Current production profiles include the adjacent bounded factsheet; historical or pure-image exceptions are identified in the linked evaluation.

Model-specific evaluations

These are not cross-model comparisons. Every unlisted model is not run.

test model result evaluation and receipts
SWE-bench Lite claude-fable-5 pxpipe 10/10; text 10/10; −65% request size paired pilot
SWE-bench Pro claude-fable-5 pxpipe 14/19; text 15/19; −60% request size paired pilot
production-history row localization google/gemini-3.6-flash text 17/30; pxpipe 18/30 positional retrieval
production-history exact row google/gemini-3.6-flash text 3/30; pxpipe 3/30 positional retrieval

The SWE-bench runner is Claude Code/Fable-specific; no other model has an ON/OFF run. Gemini's positional-retrieval sweep is directional evidence, not a general Lost-in-the-Middle result.

Capacity / density (how many chars per vision-token?)

Measured by rendering this repo’s dense fixture through the real pipeline and pricing pixels at each family’s vision rate. Multiplier = measured chars/vision-token ÷ 4 (prose text baseline). Not a model-quality score.

family window as text (@4 c/tok) as pxpipe images density multiplier
claude-fable-5[1m] (default) 1M ~4.0M ~18.9M ~18.9 c/vt (exact 28px patches) ~4.7×
google/gemini-3.6-flash 1M ~4.0M ~20.1M ~20.1 c/vt (1,078 tok/page) ~5.0×
claude-opus-5 1M ~4.0M ~18.9M ~18.9 c/vt (resolves to Fable 5’s geometry) ~4.7×

Regenerate: npx tsx scripts/gen-context-chart.ts · chart PNG docs/assets/context-window-chars.png.

The older GSM8K result is omitted because its training-data contamination can hide image misreads; the linked arithmetic evaluations use novel numbers.

How it works

model id ──► render profile ──► wrap/reflow bulk context ──► PNG[] + bounded factsheet

The proxy handles Anthropic Messages, OpenAI Responses and Chat Completions, and Google generateContent requests. It rewrites eligible bulk into image blocks and forwards the provider-native request, or bridges Anthropic Messages to a configured OpenAI-compatible provider. On Anthropic, the static prefix and prompt-cache boundary are preserved. Model-specific profiles control geometry, factsheets, history retention, and profitability, so sparse prose stays text. Events log to ~/.pxpipe/events.jsonl.

Library use (no proxy)

import { renderTextToImages, transformAnthropicMessages } from "pxpipe-proxy";

const { pages } = await renderTextToImages(toolResultText);     // pages[i].png: Uint8Array
const { body, applied, info } = await transformAnthropicMessages({
  body: requestBytes,
  model: "claude-fable-5",
});

options.keepSharp(block) pins blocks as text; options.emitRecoverable returns the originals of imaged blocks. Pure-JS runtime (Node and edge/Workers); @napi-rs/canvas is build-time only. Full API: src/core/index.ts.

Development

pnpm install && pnpm test
pnpm run build                # regenerates dist/

Windows is community-supported: primary development targets macOS/Linux, and Windows-specific fixes rely on contributor PRs (thanks @makoribrian).

FAQ

Is the headline end-to-end, or only on the requests you touched?

End-to-end, the whole bill. Most compression tools report savings only on the input slice they touched, which flatters the number. The end-to-end denominator is every production request: the small ones pxpipe correctly left untouched, all cache writes and reads, and all output tokens (which the proxy never compresses). On a 13,709-request snapshot that was 59% ($100 → ~$41); a later 8,904-compressed-request trace measured ~70%. Compressed-only runs higher (~72–74%) and is quoted separately, never as the headline. The exact figure is workload-dependent — reproduce it on your own log.

How is the math measured?

Both sides of the same request, at the same moment. For every /v1/messages POST the proxy fires a free count_tokens probe on the original uncompressed body (the counterfactual) in parallel with the real forward, and reads Anthropic's actually-billed usage block off the response. Both land in the same row of ~/.pxpipe/events.jsonl, so there is no turn-count or run-to-run confound. Dollar conversion uses Fable 5 list ratios: input ×1.0, cache write ×1.25, cache read ×0.1, output ×5. Cache pricing is applied identically to both sides, so the caching discount cancels and cannot be double-counted as "savings". Re-derive it yourself from the events log: the formula and field names are documented in src/core/baseline.ts.

What does it actually compress?

Three kinds of input blocks, each behind a profitability gate:

  1. large tool_result bodies (file reads, command output, logs) above ~6k chars of token-dense content
  2. older collapsed history: turns behind the live tail get re-rendered as image pages, recent turns always stay text
  3. the static cacheable system prompt + tool docs slab; appended non-cacheable system blocks stay live text so host custom instructions keep system-level salience

Everything else passes through byte-identical: your messages, recent turns, the model's output (it is the response, the proxy never touches it), sparse prose, and anything too small to win. Model defaults and detailed results are listed under model support and benchmarks.

Has it ever failed for real, outside the benchmarks?

Yes, once in weeks of daily use: the model recalled a person's name from imaged chat history and got it confidently wrong. No error, just a plausible wrong name. That is the documented failure mode: exact strings in imaged content are not byte-safe. Coding sessions tolerate this because the agent re-reads files before editing; pure chat recall has no such check. This failure mode is measured, not anecdotal: the legibility audit quantifies exact-string recall off rendered pages (blind reads top out at 63% on dense identifiers, with every miss predicted by a glyph-confusability matrix) and documents the shipped mitigations — page geometry clamped to the API's resample cap so billed pixels actually reach the vision encoder, and selected identifiers (SHAs, numbers) riding alongside as text.

Why are misses silent confabulations instead of read errors?

Because model vision is not OCR: the image becomes patch embeddings, never discrete characters, so there is no per-glyph confidence to fail loudly on. When pixels underdetermine a glyph, the language prior fills the gap with something plausible. Mechanism and receipts: docs/NOT-OCR.md.

Didn't DeepSeek-OCR show this doesn't hold up in practice?

No: it proved the channel works, using an encoder/decoder pair trained for the job. The skepticism dates from October 2025, when no stock production model could read dense renders; that changed with Fable 5 (0/15 verbatim hex on the prior Opus generation vs 13/15 on Fable 5, same pages). Timeline and per-model numbers: docs/NOT-OCR.md.

Why does the README read like an AI wrote it?

Because one did. Most of this repo's commits — the code and the docs — were authored by Opus/Fable agent sessions running behind pxpipe itself, reading their own collapsed history as image pages while they worked.

Additional limitations

  • PNG encoding adds latency to large requests before they leave.
  • ASCII/Latin-1 well tested; CJK works but conservatively.

Research status

Current as of 2026-07-22. The broad conclusion from the 2026-07-05 pass still holds: exact recall is limited by pixels per glyph, so rendering changes do not eliminate errors at profitable density. A later glyph-style A/B did find a useful local improvement: repainting K reduced Fable's H/K error from 47.2% to 18.7% without changing geometry or token cost. It shipped, but exact control IDs did not improve. See FINDINGS.md, 2026-07-19 entry.

Runtime canary + text re-fetch and surrogate-reader pre-flight remain untested. The release tripwire remains a resolution sweep for each new model; a model that reads production cells near 100% would permit higher density.

Effective-context benefits remain unproven. The production-history results above are directional evidence, not a general context-window or long-task accuracy claim.

Community projects

Third-party projects listed here are not maintained or supported by pxpipe.

  • pxpipe-windows — Windows support for pxpipe mitm (node-forge CA in place of openssl, Task Scheduler autostart).
  • OmniGlyph — A community-maintained project derived from pxpipe and used by OmniRoute.

License

MIT.

View on GitHub

Recent activity

commits and pull requests

Recent open issues

view all

Releases and announcements

4 total
  1. v0.11.1v0.11.1Jul 26, 2026

    ## What's Changed * Recognize gateway-routed models in telemetry and render scope by @teamchong in https://github.com/teamchong/pxpipe/pull/151 * Keep the OAuth identity as the first system block by @teamchong in https://github.com/teamchong/pxpipe/pull/153 **Full Changelog**: https://github.com/teamchong/pxpipe/compare/v0.11.0...v0.11.1

  2. v0.11.0v0.11.0Jul 26, 2026

    ## What's Changed * Add Docker deployment by @teamchong in https://github.com/teamchong/pxpipe/pull/139 * Clarify README benchmark coverage by @teamchong in https://github.com/teamchong/pxpipe/pull/140 * feat: add claude-opus-5 as a default reader; keep prompts and rules as text by @teamchong in https://github.com/teamchong/pxpipe/pull/147 * fix(responses-bridge): treat empty tool-call arguments as {} instead of crashing by @dex0shubham in https://github.com/teamchong/pxpipe/pull/131 * Add native 14px reader profiles for GPT 5.6 Sol and Grok 4.5 by @teamchong in https://github.com/teamchong/pxpipe/pull/146 **Full Changelog**: https://github.com/teamchong/pxpipe/compare/v0.10.0...v0.11.0

  3. v0.7.1v0.7.1Jul 3, 2026

    ### Fixed - **Relocated env block is now wrapped in `<system-reminder>` tags.** The volatile `# Environment` text that pxpipe moves out of the cached system prefix used to be appended to the last user message as bare prose — on an empty or short user turn it could read as the user's entire message, and models would mis-attribute it ("your message consisted of environment metadata"). The block now carries an explicit provenance header ("Context relocated by pxpipe from the system prompt … not written by the user"), fixing attribution. No cache impact: the wrapper rides the volatile tail behind all cache breakpoints (~60 chars/request).

  4. ### Added - **Per-request telemetry: `stop_reason` + safety-flag logging.** Every proxied request now records how it ended, so refusal/classifier trips are measurable instead of anecdotal. - **Headless bench:** multi-turn `claude -p` driver + `events.jsonl` scorer for fast, non-interactive A/B runs; plus a constant-cost render-style eval harness. - **`PXPIPE_DUMP_DIR`** persists rendered PNGs per request for demo/debug inspection of exactly what the model saw. - **Dashboard/factsheet:** one-time cache-create losses tagged in the recent table; factsheet carries occurrence counts with ticket-style codes. - **Demos:** `claude-sonnet-5` arm support; fable arm runs `claude-fable-5[1m]` (1M ctx) to match opus/sonnet. ### Fixed - **Imaged slab frozen at first render.** Volatile content (skill listings, cwd caches) stays out of the imaged prefix so turn-2 system sha matches turn-1 — no more silent cache-create churn between turns. - **Volatile env text relocated behind all cache breakpoints** (not just the first), plus cross-session slab stability. - **Refusal-classifier defusing:** provenance-framed slab banner and reworded tool-docs stub/header — eliminates spurious

Code frequency

additions and deletions
+53K-53KWeek of 2026-05-17: +47,380 linesWeek of 2026-05-17: -20,329 linesWeek of 2026-05-24: +2,531 linesWeek of 2026-05-24: -2,212 linesWeek of 2026-05-31: +981 linesWeek of 2026-05-31: -81 linesWeek of 2026-06-07: +52,965 linesWeek of 2026-06-07: -2,848 linesWeek of 2026-06-14: +10,677 linesWeek of 2026-06-14: -4,897 linesWeek of 2026-06-21: +5,638 linesWeek of 2026-06-21: -479 linesWeek of 2026-06-28: +4,080 linesWeek of 2026-06-28: -1,914 linesWeek of 2026-07-05: +34,069 linesWeek of 2026-07-05: -2,997 linesWeek of 2026-07-12: +24,154 linesWeek of 2026-07-12: -1,436 linesWeek of 2026-07-19: +40,898 linesWeek of 2026-07-19: -3,875 linesWeek of 2026-07-26: +3,157 linesWeek of 2026-07-26: -105 linesMay 17, 2026Jul 26, 2026
+226.5K lines added, -41.2K removed over the last year.

Commits per week

last 52 weeks
1240Week of 2025-08-03: 0 commitsWeek of 2025-08-10: 0 commitsWeek of 2025-08-17: 0 commitsWeek of 2025-08-24: 0 commitsWeek of 2025-08-31: 0 commitsWeek of 2025-09-07: 0 commitsWeek of 2025-09-14: 0 commitsWeek of 2025-09-21: 0 commitsWeek of 2025-09-28: 0 commitsWeek of 2025-10-05: 0 commitsWeek of 2025-10-12: 0 commitsWeek of 2025-10-19: 0 commitsWeek of 2025-10-26: 0 commitsWeek of 2025-11-02: 0 commitsWeek of 2025-11-09: 0 commitsWeek of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 0 commitsWeek of 2026-02-01: 0 commitsWeek of 2026-02-08: 0 commitsWeek of 2026-02-15: 0 commitsWeek of 2026-02-22: 0 commitsWeek of 2026-03-01: 0 commitsWeek of 2026-03-08: 0 commitsWeek of 2026-03-15: 0 commitsWeek of 2026-03-22: 0 commitsWeek of 2026-03-29: 0 commitsWeek of 2026-04-05: 0 commitsWeek of 2026-04-12: 0 commitsWeek of 2026-04-19: 0 commitsWeek of 2026-04-26: 0 commitsWeek of 2026-05-03: 0 commitsWeek of 2026-05-10: 0 commitsWeek of 2026-05-17: 124 commitsWeek of 2026-05-24: 20 commitsWeek of 2026-05-31: 4 commitsWeek of 2026-06-07: 46 commitsWeek of 2026-06-14: 35 commitsWeek of 2026-06-21: 30 commitsWeek of 2026-06-28: 52 commitsWeek of 2026-07-05: 19 commitsWeek of 2026-07-12: 35 commitsWeek of 2026-07-19: 21 commitsWeek of 2026-07-26: 11 commitsAug 3, 2025Jul 26, 2026
397 commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 1 commitsSun 1:00 — 0 commitsSun 2:00 — 0 commitsSun 3:00 — 0 commitsSun 4:00 — 2 commitsSun 5:00 — 2 commitsSun 6:00 — 1 commitsSun 7:00 — 0 commitsSun 8:00 — 1 commitsSun 9:00 — 2 commitsSun 10:00 — 1 commitsSun 11:00 — 0 commitsSun 12:00 — 0 commitsSun 13:00 — 1 commitsSun 14:00 — 1 commitsSun 15:00 — 2 commitsSun 16:00 — 0 commitsSun 17:00 — 6 commitsSun 18:00 — 7 commitsSun 19:00 — 1 commitsSun 20:00 — 3 commitsSun 21:00 — 0 commitsSun 22:00 — 0 commitsSun 23:00 — 0 commitsMon 0:00 — 0 commitsMon 1:00 — 0 commitsMon 2:00 — 0 commitsMon 3:00 — 0 commitsMon 4:00 — 0 commitsMon 5:00 — 0 commitsMon 6:00 — 0 commitsMon 7:00 — 0 commitsMon 8:00 — 1 commitsMon 9:00 — 1 commitsMon 10:00 — 2 commitsMon 11:00 — 5 commitsMon 12:00 — 1 commitsMon 13:00 — 1 commitsMon 14:00 — 3 commitsMon 15:00 — 10 commitsMon 16:00 — 5 commitsMon 17:00 — 9 commitsMon 18:00 — 1 commitsMon 19:00 — 1 commitsMon 20:00 — 0 commitsMon 21:00 — 4 commitsMon 22:00 — 6 commitsMon 23:00 — 4 commitsTue 0:00 — 0 commitsTue 1:00 — 0 commitsTue 2:00 — 0 commitsTue 3:00 — 0 commitsTue 4:00 — 0 commitsTue 5:00 — 0 commitsTue 6:00 — 3 commitsTue 7:00 — 2 commitsTue 8:00 — 3 commitsTue 9:00 — 2 commitsTue 10:00 — 0 commitsTue 11:00 — 4 commitsTue 12:00 — 6 commitsTue 13:00 — 5 commitsTue 14:00 — 4 commitsTue 15:00 — 5 commitsTue 16:00 — 3 commitsTue 17:00 — 3 commitsTue 18:00 — 5 commitsTue 19:00 — 10 commitsTue 20:00 — 2 commitsTue 21:00 — 8 commitsTue 22:00 — 5 commitsTue 23:00 — 3 commitsWed 0:00 — 3 commitsWed 1:00 — 0 commitsWed 2:00 — 0 commitsWed 3:00 — 0 commitsWed 4:00 — 0 commitsWed 5:00 — 0 commitsWed 6:00 — 1 commitsWed 7:00 — 7 commitsWed 8:00 — 1 commitsWed 9:00 — 10 commitsWed 10:00 — 12 commitsWed 11:00 — 2 commitsWed 12:00 — 2 commitsWed 13:00 — 5 commitsWed 14:00 — 1 commitsWed 15:00 — 4 commitsWed 16:00 — 5 commitsWed 17:00 — 1 commitsWed 18:00 — 4 commitsWed 19:00 — 4 commitsWed 20:00 — 1 commitsWed 21:00 — 2 commitsWed 22:00 — 2 commitsWed 23:00 — 5 commitsThu 0:00 — 0 commitsThu 1:00 — 0 commitsThu 2:00 — 0 commitsThu 3:00 — 0 commitsThu 4:00 — 1 commitsThu 5:00 — 1 commitsThu 6:00 — 0 commitsThu 7:00 — 0 commitsThu 8:00 — 2 commitsThu 9:00 — 0 commitsThu 10:00 — 4 commitsThu 11:00 — 1 commitsThu 12:00 — 9 commitsThu 13:00 — 3 commitsThu 14:00 — 1 commitsThu 15:00 — 4 commitsThu 16:00 — 3 commitsThu 17:00 — 11 commitsThu 18:00 — 1 commitsThu 19:00 — 3 commitsThu 20:00 — 0 commitsThu 21:00 — 4 commitsThu 22:00 — 7 commitsThu 23:00 — 8 commitsFri 0:00 — 10 commitsFri 1:00 — 2 commitsFri 2:00 — 0 commitsFri 3:00 — 0 commitsFri 4:00 — 0 commitsFri 5:00 — 2 commitsFri 6:00 — 2 commitsFri 7:00 — 0 commitsFri 8:00 — 1 commitsFri 9:00 — 4 commitsFri 10:00 — 0 commitsFri 11:00 — 4 commitsFri 12:00 — 1 commitsFri 13:00 — 4 commitsFri 14:00 — 1 commitsFri 15:00 — 1 commitsFri 16:00 — 5 commitsFri 17:00 — 0 commitsFri 18:00 — 1 commitsFri 19:00 — 2 commitsFri 20:00 — 3 commitsFri 21:00 — 1 commitsFri 22:00 — 13 commitsFri 23:00 — 4 commitsSat 0:00 — 0 commitsSat 1:00 — 0 commitsSat 2:00 — 0 commitsSat 3:00 — 0 commitsSat 4:00 — 0 commitsSat 5:00 — 0 commitsSat 6:00 — 0 commitsSat 7:00 — 0 commitsSat 8:00 — 0 commitsSat 9:00 — 1 commitsSat 10:00 — 1 commitsSat 11:00 — 1 commitsSat 12:00 — 9 commitsSat 13:00 — 3 commitsSat 14:00 — 4 commitsSat 15:00 — 1 commitsSat 16:00 — 1 commitsSat 17:00 — 0 commitsSat 18:00 — 8 commitsSat 19:00 — 0 commitsSat 20:00 — 1 commitsSat 21:00 — 6 commitsSat 22:00 — 3 commitsSat 23:00 — 4 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.

Who is committing

last 52 weeks
Maintainer commits358 (90%)
Community commits38 (10%)

396 commits in total over the last year.

DateListRankStars gained
Jul 6, 2026daily#6+8
Jul 5, 2026daily#14+24
Jul 4, 2026daily#9+33
  • freeCodeCamp/freeCodeCamp

    freeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.

    453.6K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    385.5K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    384.4K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    384.4K stars · TypeScript

  • openclaw/openclaw

    Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

    384.4K stars · TypeScript

  • anomalyco/opencode

    The open source coding agent.

    194.7K stars · TypeScript