Free tools Windows power users keep installed
One-click scans. No signup required.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use PHP’s GD extension to create a canvas, draw data-driven content, and return an encoded image. A typical endpoint validates query or database values, creates a true-color image with imagecreatetruecolor(), draws shapes and text, sends an image content type, and calls imagepng(), imagejpeg(), or another encoder. The same image can be written to a file for caching.
GD is sufficient for badges, charts, thumbnails, social cards, receipts, and text overlays. Imagick (ImageMagick through PHP) or the Imagine library are alternatives when you need different operations or an object-oriented abstraction.
What you need before writing code
- PHP with the GD extension enabled for the web-server runtime (the CLI and web-server PHP configurations can differ).
- A writable cache directory if you plan to save generated files.
- A controlled set of fonts, templates, colors, and source images. Do not let request parameters become arbitrary filesystem paths or remote URLs.
- Resource limits appropriate to your host. Image dimensions affect memory and CPU, and PHP documentation warns that system GD allocations may not be included in
memory_limit.
Check the actual build instead of assuming that a format or feature is available. This diagnostic prints GD support and the formats reported by the running extension:
<?php
header('Content-Type: text/plain; charset=utf-8');
var_export(gd_info());
Run it through the same virtual host, container, or PHP-FPM pool that will serve the image. The reported support depends on how GD and its libraries were built.
#1 Best Overall
A complete GD endpoint for a dynamic PNG
Save this as dynamic-image.php. It accepts a short name and an optional theme, draws a simple card, and streams a PNG. The allow-lists and length limits are deliberate: image endpoints are public attack surfaces when their inputs are unrestricted.
<?php
declare(strict_types=1);
$name = trim((string)($_GET['name'] ?? 'Guest'));
$name = preg_replace('/[^p{L}p{N} ._-]/u', '', $name) ?? '';
$name = mb_substr($name, 0, 40);
if ($name === '') {
$name = 'Guest';
}
$theme = (string)($_GET['theme'] ?? 'blue');
$themes = [
'blue' => ['background' => [20, 52, 112], 'accent' => [88, 190, 240]],
'green' => ['background' => [20, 96, 70], 'accent' => [112, 220, 150]],
'dark' => ['background' => [28, 28, 34], 'accent' => [240, 180, 70]],
];
$colors = $themes[$theme] ?? $themes['blue'];
$width = 1200;
$height = 630;
$image = imagecreatetruecolor($width, $height);
if ($image === false) {
http_response_code(500);
exit('Could not allocate image');
}
$background = imagecolorallocate($image, ...$colors['background']);
$accent = imagecolorallocate($image, ...$colors['accent']);
$white = imagecolorallocate($image, 255, 255, 255);
$muted = imagecolorallocate($image, 210, 220, 235);
imagefill($image, 0, 0, $background);
imagefilledrectangle($image, 0, 0, 28, $height, $accent);
imagefilledellipse($image, $width - 170, 110, 180, 180, $accent);
$title = 'Dynamic image from PHP';
$font = __DIR__ . '/fonts/DejaVuSans.ttf';
if (is_readable($font) && function_exists('imagefttext')) {
$titleBox = imageftbbox(42, 0, $font, $title);
$titleWidth = $titleBox[2] - $titleBox[0];
$titleX = (int)(($width - $titleWidth) / 2);
imagefttext($image, 42, 0, $titleX, 260, $white, $font, $title);
$nameBox = imageftbbox(58, 0, $font, $name);
$nameWidth = $nameBox[2] - $nameBox[0];
$nameX = (int)(($width - $nameWidth) / 2);
imagefttext($image, 58, 0, $nameX, 370, $white, $font, $name);
imagefttext($image, 24, 0, 80, 535, $muted, $font, 'Generated at ' . gmdate('Y-m-d H:i') . ' UTC');
} else {
// Built-in bitmap text works without FreeType, but has limited sizing.
imagestring($image, 5, 80, 230, $title, $white);
imagestring($image, 5, 80, 290, $name, $white);
imagestring($image, 3, 80, 520, 'Generated at ' . gmdate('Y-m-d H:i') . ' UTC', $muted);
}
header('Content-Type: image/png');
header('Cache-Control: public, max-age=300');
imagepng($image, null, 6);
imagedestroy($image);
Place a licensed, readable TrueType font at fonts/DejaVuSans.ttf (or change the path). The fallback uses imagestring(), which is useful for diagnostics but offers fewer fonts and sizes. imageftbbox() measures text before drawing so centered labels do not rely on guessed character widths.
Use the endpoint in HTML
<img src="/dynamic-image.php?name=Ada%20Lovelace&theme=green" width="1200" height="630" alt="Dynamic image for Ada Lovelace">
URL-encode user data. If the image is intended for an <img> tag, emit only binary image bytes after the headers; warnings, notices, debug output, or accidental whitespace can corrupt the response.
Save a file instead of streaming it
For email attachments, social-card generation, or a cache, pass a path to the encoder. Keep the destination outside user-controlled paths and create the directory during deployment.
$cacheDir = __DIR__ . '/cache';
if (!is_dir($cacheDir) && !mkdir($cacheDir, 0750, true) && !is_dir($cacheDir)) {
throw new RuntimeException('Cache directory is unavailable');
}
$key = hash('sha256', $name . '|' . $theme);
$file = $cacheDir . '/' . $key . '.png';
if (!is_file($file)) {
imagepng($image, $file, 6);
}
header('Content-Type: image/png');
readfile($file);
imagedestroy($image);
For concurrent requests, write to a temporary file and rename it atomically, or use a lock, so a reader never receives a partially encoded file. Include every visual input, font version, and template version in the cache key. Add an expiration policy when the underlying data changes.
Rank #2
Other GD operations you can combine
Load a controlled template
Use a known loader such as imagecreatefrompng() or imagecreatefromjpeg() for files you select from an allow-list. Copy or resample it onto the destination canvas, then draw dynamic content. Never map a query-string filename directly to a filesystem path.
Resize and crop
Create a destination canvas at the required dimensions and call imagecopyresampled(). Calculate source and destination rectangles explicitly to preserve aspect ratio; reject dimensions above your application’s pixel limit before allocating either canvas.
Recommended Free Tools
Choose an output format
PNG is appropriate for crisp text, transparency, and flat graphics. JPEG is generally suitable for photographic content and requires a quality value. WebP can reduce size when the installed build reports support. Use gd_info() and handle an unavailable encoder rather than assuming the manual’s format table applies to your server.
Transparency
For a transparent PNG, call imagealphablending($image, false), imagesavealpha($image, true), and fill with a fully transparent color before drawing. Test compositing against both light and dark backgrounds.
When GD is not the right layer
| Option | Good fit | Checks and trade-offs |
|---|---|---|
| GD | Raster cards, text overlays, thumbnails, simple charts, and direct browser output. | Encoders and FreeType functions depend on the server build; inspect gd_info(). |
| Imagick | Workflows needing ImageMagick operations or broad format handling. | Requires the PHP extension and ImageMagick environment. PHP labels the extension experimental; verify versions, delegates, enabled formats, and security policy in production. |
| Imagine | An object-oriented API with a choice of GD2, Imagick, or Gmagick drivers. | The selected driver and library still determine supported operations and requirements. Confirm the current release and its compatibility. |
PHP describes ImageMagick as able to read, convert, and write more than 100 formats, including examples such as DPX, EXR, JPEG-2000, PDF, SVG, and TIFF. That breadth does not remove the need to restrict formats and operations. ImageMagick delegates and policy files should be reviewed before accepting untrusted files.
Uploads and untrusted inputs
A filename and client-supplied MIME type do not prove that an upload is a safe image. Apply an upload-size limit, verify the upload using PHP’s upload handling APIs, decode it, check its dimensions, and store it outside executable directories. Reject oversized pixel counts before resizing or compositing. Consider a processing timeout and isolate generated files from application code.
Remote URL loaders are especially risky: they can enable server-side request forgery, slow responses, or unexpected formats. Prefer template IDs and server-side allow-lists. If a business requirement truly needs remote assets, restrict hosts, schemes, redirects, response size, and time.
Performance, reliability, and cost control
- Cache deterministic results using a key containing all content and rendering inputs.
- Do not allocate a full-size canvas until dimensions have passed validation.
- Reuse a pre-rendered background or template when only a small text region changes.
- Measure generation time and peak process memory in your deployment environment; documentation alone cannot predict behavior for every build.
- Return a stable error response (for example, HTTP 400 for invalid parameters and HTTP 500 for allocation or encoder failures) instead of emitting a corrupt image.
- Keep logs separate from the binary response. Send diagnostics to a server log.
GD’s memory behavior deserves special attention: PHP documentation states that system versions of GD may allocate outside Zend’s memory manager, so memory_limit may not account for those allocations. Enforce your own pixel and workload limits in addition to PHP settings.
Troubleshooting common failures
“Call to undefined function imagecreatetruecolor()”
GD is not enabled in the PHP runtime serving the request. Install or enable the GD package for that runtime, restart PHP-FPM or the web server, and confirm with gd_info(). Checking only CLI PHP can inspect the wrong configuration.
Text is missing or the font call fails
FreeType support or the font file is unavailable. Check function_exists('imagefttext'), verify the absolute font path and file permissions, and retain a fallback such as imagestring() if limited bitmap text is acceptable.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallRank #4
The browser shows a broken image
Look for notices, warnings, BOM characters, or debug output before the image bytes. Confirm the response’s Content-Type, disable display errors for production responses, and inspect the raw response with a command-line HTTP client.
PNG, WebP, or JPEG output fails
The encoder may not be compiled into this GD build, or the destination is not writable. Check gd_info(), test a known writable path, and provide a supported fallback format.
Requests run out of memory or time out
Reduce maximum dimensions and source pixel counts, avoid repeated intermediate canvases, reject huge uploads before decoding, and cache repeated renders. If using Imagick, review ImageMagick resource limits and policy as well.
Generated files contain stale data
Your cache key or expiration rule does not include a changed input. Version the template and font in the key, or invalidate entries when the source record changes.
Or skip the browser setup
If your PHP endpoint already returns the image, you can capture its rendered result with ScreenshotNeo instead of configuring a headless browser. It accepts the page before capture, removes cookie banners, newsletter popups, and chat widgets, and bills only clean shots; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
One request returns a PNG, JPEG, WebP, or PDF:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your.example.com/dynamic-image.php?name=Ada%20Lovelace -o shot.webp
See the ScreenshotNeo API documentation for authentication and options. The same endpoint can be called from PHP, Python, or Node.js:
// PHP
$url = 'https://api.screenshotneo.com/v1/shot';
$query = http_build_query(['access_key' => 'YOUR_API_KEY', 'url' => 'https://your.example.com/dynamic-image.php?name=Ada%20Lovelace']);
$data = file_get_contents($url . '?' . $query);
file_put_contents('shot.webp', $data);
# Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://your.example.com/dynamic-image.php?name=Ada%20Lovelace"}, timeout=90)
open("shot.webp", "wb").write(r.content)
// Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://your.example.com/dynamic-image.php?name=Ada%20Lovelace' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Every response reports page and billing status in X-Page-Verdict and X-Billed headers. Plans include 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000. Sign up for the free ScreenshotNeo plan.
Frequently asked questions
Can PHP generate an image without saving it first?
Yes. Send the correct content type and call the encoder without a filename, as the GD example does; PHP writes the encoded bytes to the response stream.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Should I use SVG instead of GD?
GD creates raster images. Choose another renderer when your output must remain vector-based or when your required operations are outside GD’s drawing and encoding features.
Is a timestamp in a generated image cache-friendly?
No. A changing timestamp makes each render a different result. Omit it for stable caching or include an explicit time bucket when periodic updates are required.
Can I trust the image dimensions reported by an upload?
Use dimensions obtained after decoding and enforce limits before expensive processing. Client metadata alone is not a sufficient validation step.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

