Create a gist now

Instantly share code, notes, and snippets.

Automatically generated images via JavaScript Canvas API

This is a quick prototype I knocked together (in under an hour) for a family member who had a business idea that he wasn't sure how to implement.

I had never used the HTML5 Canvas API before but it sounded like it could be a good fit for what he was trying to achieve.

It was a fun little thing to work on as it gave me a chance to play around with using Canvas and I discovered that text written in Canvas cannot (currently) wrap to the width of the Canvas element. Although I believe there may now be some new additions to the API which works around this wrapping issue.

<!doctype html>
<html dir="ltr" lang="en">
<meta charset="utf-8">
<title>HTML5 Canvas</title>
<style type="text/css">
body {
font: normal large Helvetica, sans-serif;
#card {
border: 1px solid red;
<ul id="status"></ul>
<script type="text/javascript">
var doc = document,
status = doc.getElementById("status"),
canvas = doc.createElement("canvas"),
counter = 0;
cards = [
{ img: "card1", txt: "card 1 text" },
{ img: "card2", txt: "card 2 text" },
{ img: "card3", txt: "card 3 text" }
canvas.width = 960;
canvas.height = 739;
cards.forEach(function (card) {
var img = new Image(); = "Card" + ++counter;
img.src = card.img + ".png";
img.onload = function(){
// First argument of drawImage (see further down) should be an image and not a string
// So we must replace string in object to be the image just created
card.img = img;
// Now create a new canvas for this image
function createCanvas(card) {
var newCanvas = canvas.cloneNode(false), // shallow clone
ctx = newCanvas.getContext("2d");
// Now load relevant image into this canvas
loadImageIntoCanvas(card, newCanvas, ctx);
function loadImageIntoCanvas (card, newCanvas, ctx) {
// Load the specified image into the canvas (image, x, y, width[optional], height[optional])
ctx.drawImage(card.img, 0, 0, 700, 400); // I've purposedly squashed the image
// Now image is loaded we'll draw some text into the canvas
writeText(card, newCanvas, ctx);
function writeText (card, newCanvas, ctx) {
// Place text into the canvas (beware the text can't wrap so you might need multiple 'fillText' calls)
ctx.fillStyle = "#CC0000";
ctx.font = "italic bold 25px Helvetica";
ctx.fillText(card.txt, 125, 180);
// Now lets save this image
saveImage(card, newCanvas, ctx);
function checkHTTPSuccess (xhr) {
try {
// If no server status is provided, and we're actually
// requesting a local file, then it was successful
return !xhr.status && location.protocol == 'file:' ||
// Any status in the 200 range is good
( xhr.status >= 200 && xhr.status < 300 ) ||
// Successful if the document has not been modified
xhr.status == 304 ||
// Safari returns an empty status if the file has not been modified
navigator.userAgent.indexOf('Safari') >= 0 && typeof xhr.status == 'undefined';
} catch(e){
// Throw a corresponding error
throw new Error("httpSuccess Error = " + e);
// If checking the status failed, then assume that the request failed too
return false;
function getHTTPData (xhr, type) {
if (type === 'json') {
return JSON.parse(xhr.responseText);
else if (type === 'html') {
return xhr.responseText;
else if (type === 'xml') {
return xhr.responseXML;
// Attempt to work out the content type
else {
// Get the content-type header
var contentType = xhr.getResponseHeader("content-type"),
data = !type && contentType && contentType.indexOf("xml") >= 0; // If no default type was provided, determine if some form of XML was returned from the server
// Get the XML Document object if XML was returned from the server,
// otherwise return the text contents returned by the server
data = (type == "xml" || data) ? xhr.responseXML : xhr.responseText;
// Return the response data (either an XML Document or a text string)
return data;
function onSuccessfulImageProcessing (card, newCanvas, ctx, response) {
// Clean-up after ourselves to save application memory
newCanvas = null;
ctx = null;
// Update status list
var txt = doc.createTextNode( + " has now been generated."),
li = doc.createElement("li");
function saveImage(card, newCanvas, ctx) {
// Can't use standard library AJAX methods (such as…)
// data: "imgdata=" + newCanvas.toDataURL()
// Not sure why it doesn't work as we're only abstracting an API over the top of the native XHR object?
// To make this work we need to use a proper FormData object (no data on browser support)
var formData = new FormData();
formData.append("imgdata", newCanvas.toDataURL());
var xhr = new XMLHttpRequest();"POST", "saveimage.php");
// Watch for when the state of the document gets updated
xhr.onreadystatechange = function(){
// Wait until the data is fully loaded, and make sure that the request hasn't already timed out
if (xhr.readyState == 4) {
// Check to see if the request was successful
if (checkHTTPSuccess(xhr)) {
// Execute the success callback
onSuccessfulImageProcessing(card, newCanvas, ctx, getHTTPData(xhr));
else {
throw new Error("checkHTTPSuccess failed = " + e);
xhr.onreadystatechange = null;
xhr = null;
// Get data
$data = $_POST['imgdata'];
// Remove the "data:image/png;base64," part
$uri = substr($data, strpos($data, ",") + 1);
// Save the file to the machine (try to make it unique)
file_put_contents("./Generated/card-" . mt_rand() . ".png", base64_decode($uri));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment