The Sharing: Part 3. Generating Dynamic Meta Images with ImageMagick on Lambda
Lambda, I’m Home!

This is Part 3 of a series of posts about generating and sharing dynamic images on the web. All work and no hack makes Lee a dull boy.
As I mentioned in Part 1, the one-click, non-authenticated, image share to Twitter and Facebook does not exist. 😔 Instead, we must utilize the meta functionality of both platforms to get them to share an image as part of a on-click URL share. That’s Cards for Twitter and OpenGraph tags for Facebook, respectively. Rather than generating and storing every dynamic image, we’re going to use a Lambda function to return an image as part of a real-time response. In addition, we’ll use another function to dynamically serve companion HTML share pages which contain all the meta tags required.
Part 3. Lambda, I’m Home!
Serverless Prerequisites
I use the Serverless framework to setup my AWS projects due to its ease of use and ability to scaffold up and tear down projects as needed. I would highly recommend the Serverless Stack tutorial if it is new to you. I found myself learning the basics after a few tries and then using the ease of the Serverless framework to discover areas of AWS I didn’t even know existed. Anyway, there are two things we’ll need to do to make sure our Lambda function works properly: Make sure ImageMagick is available to our function and activate binary media types so our function can return an image.
Layers are the way Lambda allows you share common components between functions. It is also the ideal way to make sure the ImageMagick library is bundled with your function. Here’s a link to an ImageMagick layer which you can deploy to your AWS account. I had to seek out a special ImageMagick layer which was compiled with free type as I needed typography support. Once the layer is deployed, make note of the ARN because you’ll need it for your Serverless config file.
We’ll also need to activate binary media types. This is real easy with Serverless and simply requires us to add the appropriate block to our config. Here’s a look at my full serverless.yml
file.
service: thesharing-apiprovider:
name: aws
runtime: nodejs12.x
apiGateway:
binaryMediaTypes:
- '*/*'functions:
share:
handler: share.image
events:
- http:
path: share/{name}
method: get
package:
include:
- src/**
layers:
- IMAGEMAGICK_ARN
When setting up the image generation function (share) in the config, we include a reference to the ImageMagick Lambda layer and a folder of assets (src) we’ll need to generate our image.
Using GraphicMagick Node Module to Generate Image on Lambda
In our serverless.yml
file, we configured a function, accessible via URL, which would serve up our dynamic image. Let’s go ahead and get that programmed. First, we’ll want to install the GraphicsMagick node package, as we’ll be using it to interface with ImageMagick. We’re also going to install gm-base64
as we’ll need to return our GraphicsMagick generated images as Base64 encoded strings.
Make sure you have a package.json in your and install.
npm i gm -s
npm i gm-base64 -s
Then, back in our function, we can require both packages, making sure to activate imageMagick
so gm uses it.
let gm = require('gm').subClass({
imageMagick: true
})require('gm-base64')
Here’s a look at the function itself.
module.exports.image = (event, context, callback) => {
let name = event.pathParameters.name ? decodeURI(event.pathParameters.name) : "Jack Torrance"gm('src/thesharing-meta.jpg')
.fill('black')
.font('src/sf-movie-poster.ttf', 64)
.drawText(350, 546, name.toUpperCase())
.toBase64('png', function (error, base64) {
const response = {
statusCode: 200,
isBase64Encoded: true,
headers: {
'Content-Type': 'image/png'
},
body: base64
} callback(null, response)
})
}
First, we check to see if a dynamic name was passed via the endpoint path and if not, we default to “Jack Torrance.” Then, gm
opens up the thesharing-meta.jpg file which serves as the base of our dynamic image. Our goal here is to simply write the dynamically provided name under the “starring” subtitle in the image. So, we set the fill
to black and the font
to SF Movie Poster with a size of 64. After a bit of trial and error, we figure out the x
and y
coordinates we can pass to the drawText
function, alongside the name in uppercase. Finally, the image is returned as a base64 encoded png and the Lambda function responds with it in the body, setting isBase64Encoded
to true.
You should now be able to deploy your Serverless project and visit the function endpoint, adjusting the name as you wish. The endpoint should return a png image. Depending on your use case, you’ll want to adjust and parse the dynamic parameters carefully.
Dynamically Serve HTML Meta on Netlify

The last thing we need to do is write another Lambda function which can dynamically generate a HTML file containing all the meta tags, for any dynamic name. While I could also do this in the same Serverless stack, I’m choosing to write the function on Netlify instead because that’s where I’ll be serving the app itself. This gives me a bit easier access to a function I find is much more part of the “frontend” of the app. Netlify has great docs on setting up functions so let’s just take a look at the function code itself.
We are simply extracting the name parameter from the URL and passing it to some dynamic HTML, containing the Twitter and Facebook meta tags, each time our Lambda image generator endpoint URL is needed. For those users who click through from Twitter or Facebook to this dynamic page, we redirect them to the root of our campaign.
Simplifying The Function URL
My Netlify function is named meta-share and is accessible from /.netlify/functions/meta-share
. What if we wanted to instead use something like /share
to keep things nice and simple when publicly shared? We can do this using Netlify redirects. We’ll simply add the following line to our netlify.yml
file and Netlify will take care of the redirect.
[[redirects]]
from = "/share/:name"
to = "/.netlify/functions/meta-share?name=:name"
status = 200
Testing Dynamic Share Pages
You can see both the image generator and dynamic share page in action by passing a share url to the Twitter card validator or Facebook sharing debugger. Just pass the following url.
https://thesharing.netlify.app/share/NAME
Note: If you want to use a first and last name, just make sure to replace all spaces with %20
. Also, you may need to click the preview or debug buttons a few times for brand new urls.