fastchart
June 5, 2026 ยท View on GitHub
Native C PHP extension. 26 chart types behind a modern OO API with
fluent setters and final classes. Line, area, bar, scatter, bubble,
pie, radar, polar, surface, contour, gauge, gantt, box-plot, treemap,
funnel, waterfall, heatmap, linear meter, plus a deep StockChart
(seven candle styles, SMA / EMA / WMA overlays, volume + indicator
panes).
SVG is the canonical render format. PNG / JPG / WebP outputs flatten
text to glyph paths, run plutovg over the resulting SVG, and encode
the RGBA buffer with libpng / libjpeg-turbo / libwebp. The same chart
object serves a sharp <svg> for dashboards or a PNG for emails
without rebuilding state. renderToFile() picks the encoder from the
extension; renderPng() / renderJpeg() / renderWebp() /
renderSvg() return bytes in-process.

Live gallery โ. Side-by-side SVG / PNG / JPG / WebP renders for every chart family, with the source PHP shown above each row.
Status
v1.1: confidence-band area charts, cone-style funnels, smooth polar
curves with overlay vectors, log-scale bubble plots, HTML image-map
hot-spots on Bar / Pie / Scatter (setImageMap + getImageMap).
v1.0 dropped libgd as a runtime dependency, rebuilt rasterization
around vendored plutovg, and replaced draw($canvas) with
renderSvg/Png/Jpeg/Webp + renderToFile. 26 chart types, 2-class
Symbol family, 138 phpt tests. See CHANGELOG.md
for the full breaking-change list.
Install
Via PIE (recommended)
PIE handles the configure / make / install cycle for you against your active PHP install:
pie install iliaal/fastchart
PIE picks up the latest tagged release from Packagist and respects
your platform's pkg-config for the FreeType / libpng /
libjpeg-turbo / libwebp probes. After install, enable the
extension via your distribution's mechanism (e.g.
docker-php-ext-enable fastchart on the official PHP images, or
add extension=fastchart to php.ini).
From source
Build manually against the PHP install you want to extend:
phpize
./configure --enable-fastchart
make -j
make test
Strict-warnings dev build (recommended for contributors):
./configure --enable-fastchart --enable-fastchart-dev
Runtime check:
php -d extension=./modules/fastchart.so \
-r 'echo FastChart\Chart::version(), PHP_EOL;'
Requirements
- PHP 8.1 or later (NTS or ZTS).
- FreeType development headers (
libfreetype-dev/freetype-devel). Required, since text rendering depends on FreeType. - libpng / libjpeg-turbo / libwebp development headers. Each is
optional; config.m4 probes them independently via pkg-config and
the corresponding
renderPng()/renderJpeg()/renderWebp()is wired up only for libs that resolve at build time. A missing lib turns the matching method into a "format not compiled in" Error at call time; SVG output stays available regardless.phpinfo()reports the resolved version of each lib (or(not compiled in)) so you can audit a build. - plutovg + plutosvg are vendored under
vendor/; no separate install required.
Quick start
The shortest path is the renderToFile() helper, which picks the
encoder from the file extension:
(new FastChart\LineChart(640, 320))
->setTitle('Daily active users')
->setSeries([['data' => [820, 940, 870, 1020, 1180, 1250, 1340]]])
->setCategoryLabels(['Mon','Tue','Wed','Thu','Fri','Sat','Sun'])
->renderToFile('/tmp/dau.png');
renderPng(), renderJpeg(), and renderWebp() return the encoded
bytes if you need them in memory. Raster compression knobs:
$chart->setJpegQuality(75); // sticks on the chart instance;
$chart->renderJpeg(); // ...used by every render call
$chart->renderJpeg(95); // per-call override (1..100)
$chart->renderWebp(); // default quality 90
$chart->renderWebp(60); // smaller, lossier (1..100)
$chart->renderToFile('/tmp/out.webp'); // setJpegQuality only affects
// .jpg; renderToFile uses the
// built-in WebP default
PNG is always lossless; there's no quality knob. The encoder is libpng with default filter selection.
WebP has four encoder modes selectable via setWebpMode(). The
default WEBP_DRAWING is tuned for chart-shaped content (flat
fills, sharp edges); see the table in
docs/examples/50_webp_modes.php
for the speed/size trade-offs each mode picks.
$chart->setWebpMode(FastChart\Chart::WEBP_LOSSLESS); // archival
$chart->setWebpMode(FastChart\Chart::WEBP_FAST); // preview pipelines
$chart->setWebpMode(FastChart\Chart::WEBP_PHOTO); // photo backgrounds
$chart->setWebpMode(FastChart\Chart::WEBP_DRAWING); // back to default
Call renderSvg() on the same chart object for vector output:
dashboards, print, anywhere infinite-zoom matters.
$chart = (new FastChart\LineChart(640, 320))
->setTitle('Daily active users')
->setSeries([['data' => [820, 940, 870, 1020, 1180, 1250, 1340]]])
->setCategoryLabels(['Mon','Tue','Wed','Thu','Fri','Sat','Sun']);
$svg = $chart->renderSvg(); // full <?xml ?><svg>...</svg>
$chart->renderToFile('/tmp/dau.svg'); // same, written to disk
// Stitch several charts into one outer SVG document:
$fragment = $chart->drawSvgFragment(); // <g class="fastchart">...</g>
Construction is identical for every output format; only the final
render call differs. By default, SVG text is flattened to glyph
outline paths (SVG_TEXT_PATHS mode). The resulting SVG is self-
contained and renders identically in any viewer or rasterizer.
For smaller files with selectable text, switch to native <text>
mode:
$chart->setSvgTextMode(FastChart\Chart::SVG_TEXT_NATIVE);
$svg = $chart->renderSvg(); // ~30% smaller; needs consumer text support
Raster outputs (PNG/JPG/WebP) always use the PATHS mode internally; plutovg has no text support of its own, so glyph flattening is what makes labels appear in the rasterized output.
Three static methods on FastChart\Chart rasterize caller-supplied
SVG bytes through the same plutovg + libpng / libjpeg-turbo /
libwebp pipeline. Useful for round-tripping renderSvg() output, or
for stitching multiple drawSvgFragment() calls into one outer SVG
and rasterizing the result, all in-process:
$png = FastChart\Chart::svgToPng($svg);
$jpg = FastChart\Chart::svgToJpeg($svg, 88, 0xFFFFFF); // bg + quality
$webp = FastChart\Chart::svgToWebp($svg, 90, FastChart\Chart::WEBP_LOSSLESS);
Output dimensions come from the SVG's width / height / viewBox.
SVG <text> elements render blank because plutovg has no text
engine, so text must be path-flattened first (fastchart's own SVG
builder does this automatically). See
docs/examples/51_svg_to_raster.php
for a runnable demo and
docs/specs/svg-to-raster.md for the
full contract.
๐ Performance
Median in-memory render time at 1920ร1080 on a single core (Intel i9-13950HX, PHP 8.4 release build NTS -O2, default font + DPI). SVG is the canonical output; PNG / WebP / JPG go through the same SVG build, then plutosvg + plutovg rasterize, then the format encoder (libpng / libwebp / libjpeg-turbo). The raster columns therefore add the rasterize cost on top of the SVG-only number.
| Chart | SVG ms | PNG ms | WebP ms | JPG ms |
|---|---|---|---|---|
| AreaChart | 0.12 | 45.19 | 34.04 | 11.45 |
| BarChart | 0.22 | 43.54 | 35.18 | 12.35 |
| BoxPlot | 0.09 | 40.26 | 30.74 | 9.95 |
| BubbleChart | 0.06 | 52.84 | 44.18 | 14.84 |
| ContourChart | 0.11 | 48.46 | 41.14 | 12.97 |
| Funnel | 0.08 | 40.15 | 31.59 | 9.53 |
| GanttChart | 0.12 | 40.08 | 30.98 | 10.22 |
| GaugeChart | 0.02 | 44.75 | 33.78 | 10.19 |
| Heatmap | 0.05 | 43.28 | 35.62 | 11.85 |
| LineChart | 0.11 | 45.45 | 37.23 | 11.81 |
| LinearMeter | 0.02 | 38.64 | 29.10 | 8.98 |
| PieChart | 0.06 | 44.42 | 33.84 | 11.75 |
| PolarChart | 0.02 | 47.60 | 37.10 | 11.48 |
| RadarChart | 0.06 | 47.89 | 36.48 | 13.39 |
| ScatterChart | 0.11 | 43.05 | 32.48 | 10.38 |
| StockChart | 0.20 | 46.00 | 40.86 | 14.19 |
| SurfaceChart | 0.06 | 40.09 | 34.50 | 10.35 |
| Treemap | 0.11 | 39.76 | 31.74 | 9.74 |
| Waterfall | 0.09 | 39.21 | 32.05 | 10.08 |
SVG stays well under a quarter-millisecond across the board (0.02 to
0.22 ms) because there's no rasterization; the backend appends strings
into a smart_str via an allocation-free integer/fraction number
emitter.
The raster encoders split into three bands: JPG fastest (9-15 ms,
libjpeg-turbo with 4:2:0 subsampling + SSSE3/NEON RGBA-pack), WebP
middle (29-44 ms, libwebp with WEBP_PRESET_DRAWING + method=2 +
multi-thread), PNG slowest (38-53 ms, libpng's deflate dominates).
All four formats stay under 55 ms at 1080p on one thread.
These numbers reflect the optimization series in v1.1.x (allocation-free
SVG number formatting, glyph outline cache, opaque-detect un-premultiply
with SSSE3/NEON shuffle, deferred text overlays, larger FT raster pool);
see
optimization.md for the per-finding breakdown.
Repro the numbers locally:
php -d extension=./modules/fastchart.so docs/bench/bench.php
Iteration count via FC_BENCH_ITERS (default 50). Bench source at
docs/bench/bench.php.
What you can render
26 chart classes plus a 2-class symbology family, all under the
FastChart\ namespace. Each name links to its rendered example image:
- Cartesian:
LineChart,AreaChart,BarChart(vertical, horizontal, stacked, grouped, floating, layered),ScatterChart,BubbleChart. - Financial:
StockChartwith seven candle styles (STYLE_CANDLE,STYLE_BAR,STYLE_DIAMOND,STYLE_I_CAP,STYLE_HOLLOW,STYLE_VOLUME,STYLE_VECTOR), SMA / EMA / WMA overlays, optional volume pane and custom indicator panes (RSI, MACD, Bollinger, OBV, stochastic, PSAR). - Non-Cartesian:
RadarChart,PolarChart,SurfaceChart,ContourChart. - Specialised:
PieChart(with optional donut hole + leader lines),GaugeChart,LinearMeter,GanttChart,BoxPlot,Treemap,Funnel,Waterfall,Heatmap,BulletChart,ParetoChart,CalendarHeatmap. - Hierarchical / flow:
SunburstChart,SankeyChart,MarimekkoChart,VectorChart. - Symbology:
Code128(1D barcode, ISO/IEC 15417, auto-switching A/B/C subsets, optional human-readable text),QrCode(2D matrix code, ISO/IEC 18004, ECC L/M/Q/H, versions 1..40).
Cross-cutting features available on most chart types:
- TrueType / OpenType labels via
setFontPath()(and per-rolesetTitleFont(),setAxisFont(),setLabelFont()). - Light and dark themes (
THEME_LIGHT,THEME_DARK); per-series colors viasetSeriesColors(); full custom palettes viasetPalette(). - Legend positioning (
LEGEND_TOP_RIGHT,_TOP_LEFT,_BOTTOM_RIGHT,_BOTTOM_LEFT,_NONE). - Annotations: plot bands, vertical bands, horizontal / vertical lines, text labels, icon plots, error bars, zones.
- Strict-mode input validation (
setStrict(true)rejects malformed series with aTypeErrorinstead of silently coercing to NaN). - Background images, drop shadows, anti-aliased lines and markers.
- Image map output (
getImageMap()returns category-aligned rectangles for HTML overlay).
Examples
A gallery of code + rendered chart pairs lives in
docs/README.md. Forty-two runnable scripts in
docs/examples/ regenerate the images and exercise
every public method on the API surface.
Public classes
All under the FastChart\ namespace:
Chart: abstract base. Carries shared geometry / theme / font / legend / annotation setters, theversion()static, and the chart- family enums (themes, candle styles, legend positions, line styles, marker styles, MA kinds).LineChart,AreaChart,BarChart,ScatterChart,BubbleChart: series-based plots.PieChart: slice-based, with optional donut hole.StockChart: OHLC(V) candlesticks, moving-average overlays, volume + indicator panes.RadarChart,PolarChart,SurfaceChart,ContourChart: non-Cartesian plots.GaugeChart,LinearMeter: single-value readouts with zoned ranges.GanttChart: time-axis task bars with dependency links and milestones.BoxPlot: five-number summaries with per-category outliers.Treemap,Funnel,Waterfall: value-encoded layouts (rectangle packing, stage drop-off, signed-delta running totals).Funnelsupports a triangle-with-bands layout viasetStyle(STYLE_PYRAMID)for callers who want the classic pyramid shape.Heatmap: 2D grid with linear color-ramp interpolation.
Every setter returns static, so a single fluent expression configures
and emits a chart.
The Symbol family lives parallel to Chart (no shared base, since
axes / palettes / plot rect have no meaning for a barcode):
Symbol: abstract base for all 1D + 2D codes. Carries shared setters:setSize(),setData(),setQuietZone(),setForeground(),setBackground(),setTransparentBackground(),setDpi(),setSvgTextMode(),setJpegQuality(), plus the samerenderPng()/renderJpeg()/renderWebp()/renderSvg()/drawSvgFragment()/renderToFile()helpers asChart. Reload viaimagecreatefromstring()to composite onto an existing canvas.Barcode: abstract 1D linear-barcode base.Code128(extendsBarcode): ISO/IEC 15417, alphanumeric, three subsets (A: control + uppercase, B: full ASCII printable, C: digit pairs). Auto-switches between subsets to minimise encoded length; mod-103 checksum appended automatically.setShowText(true)renders the human-readable payload below the bars using the auto-detected default font.QrCode(extendsSymbol): ISO/IEC 18004, four error-correction levels (ECC_L~7%,ECC_M~15%,ECC_Q~25%,ECC_H~30%), versions 1..40. Encoder is the vendored nayuki/QR-Code-generator C library.setMinVersion()/setMaxVersion()pin the symbol size; the encoder picks the smallest version that fits within the range. Input must be valid UTF-8.
๐ PHP Performance Toolkit
Companion native PHP extensions for high-throughput PHP workloads:
- php_excel: native Excel I/O. 7-10ร faster than PhpSpreadsheet, full XLS/XLSX with formulas, formatting, and styling. Powered by LibXL.
- mdparser: native CommonMark + GFM parser. 15-30ร faster than pure-PHP alternatives, full CommonMark 0.31 compliance.
- php_clickhouse: native ClickHouse client speaking the wire protocol directly. Picks up where SeasClick left off.
License
BSD 3-Clause for the extension itself; see LICENSE.
Vendored third-party code (all MIT):
vendor/plutovg/: Samuel Ugochukwu's plutovg 2D rasterizer. Seevendor/plutovg/LICENSE.vendor/plutosvg/: Samuel Ugochukwu's plutosvg SVG document parser. Seevendor/plutosvg/LICENSE.vendor/qrcodegen/: nayuki's QR-Code-generator (C variant). Seevendor/qrcodegen/LICENSE.
SPDX: (BSD-3-Clause AND MIT).
Follow @iliaa on X โข Blog โข If this saved you a chart-rendering microservice, โญ star it!