iliaal / fastchart
Native C PHP extension for fast chart rendering: 26 chart families (line, bar, pie, scatter, bubble, stock with technical indicators, radar, polar, surface, contour, treemap, funnel, waterfall, heatmap, gauge, linear meter, gantt, box plot, area, bullet, pareto, calendar heatmap, sunburst, sankey, m
Package info
Language:C
Type:php-ext
Ext name:ext-fastchart
pkg:composer/iliaal/fastchart
Requires
- php: >=8.3
This package is auto-updated.
Last update: 2026-05-30 02:23:11 UTC
README
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.3 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.85 | 45.19 | 34.04 | 11.45 |
| BarChart | 1.55 | 43.54 | 35.18 | 12.35 |
| BoxPlot | 0.62 | 40.26 | 30.74 | 9.95 |
| BubbleChart | 0.32 | 52.84 | 44.18 | 14.84 |
| ContourChart | 0.39 | 48.46 | 41.14 | 12.97 |
| Funnel | 0.60 | 40.15 | 31.59 | 9.53 |
| GanttChart | 0.82 | 40.08 | 30.98 | 10.22 |
| GaugeChart | 0.16 | 44.75 | 33.78 | 10.19 |
| Heatmap | 0.21 | 43.28 | 35.62 | 11.85 |
| LineChart | 0.69 | 45.45 | 37.23 | 11.81 |
| LinearMeter | 0.16 | 38.64 | 29.10 | 8.98 |
| PieChart | 0.40 | 44.42 | 33.84 | 11.75 |
| PolarChart | 0.17 | 47.60 | 37.10 | 11.48 |
| RadarChart | 0.48 | 47.89 | 36.48 | 13.39 |
| ScatterChart | 0.67 | 43.05 | 32.48 | 10.38 |
| StockChart | 1.22 | 46.00 | 40.86 | 14.19 |
| SurfaceChart | 0.29 | 40.09 | 34.50 | 10.35 |
| Treemap | 0.73 | 39.76 | 31.74 | 9.74 |
| Waterfall | 0.68 | 39.21 | 32.05 | 10.08 |
SVG is sub-millisecond to ~1.5 ms across the board because there's
no rasterization; the backend appends strings into a smart_str.
The raster encoders split into three bands: JPG fastest (9-15 ms,
libjpeg-turbo with 4:2:0 subsampling + SSSE3 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 (glyph
outline cache, opaque-detect un-premultiply with SSSE3 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!
