Cloudflare Docs
Visit Constellation on GitHub
Set theme to dark (⇧+D)

Make your first Constellation Worker

In this guide, you will build an image classification application powered by a Constellation inference engine and the SqueezeNet 1.1 ONNX model. SqueezeNet is a convolutional neural network (CNN) that was pre-trained on more than one million images from the open-source ImageNet database and can classify images into 1,000 categories. SqueezeNet compares to AlexNet, one of the original CNNs and benchmarks for image classification, by being faster and smaller, while still achieving similar levels of accuracy.

​​ Prerequisites

Before continuing, make sure you have:

​​ Create a new Constellation project

Generate a new Constellation project named image-classifier by running the create command. Then run list to review the details of your newly created project:

$ npx wrangler constellation project create "image-classifier" ONNX
$ npx wrangler constellation project list
id │ name │ runtime │
│ 2193053a-af0a-40a6-b757-00fa73908ef6 │ image-classifier │ ONNX │

​​ Create a new Worker

Create a new Worker named image-classifier-worker. You will install Wrangler, the developer platform CLI, for Constellation which is still in beta.

$ mkdir image-classifier-worker
$ cd image-classifier-worker
$ npm init -f
$ npm install [email protected] --save-dev
$ npx wrangler init

Answer Wrangler’s configuration questions:

Would you like to use git to manage this Worker?: N
Would you like to use TypeScript? Y
Would you like to install the type definitions for Workers into your package.json?: Y
Would you like to create a Worker at src/index.ts?: Fetch handler
Would you like us to write your first test with Vitest?: N

​​ Bind your Constellation project to your Worker

In your image-classifier-worker, find your wrangler.toml file.

Bindings allow your Workers to interact with resources on the Cloudflare developer platform, such as your Constellation project. Create a binding between your image-classifier Constellation project and your image-classifier-worker Worker in your image-classifier-worker Worker’s wrangler.toml configuration file.

Substitute the project_id with the project ID you generated after running npx wrangler constellation project list in Create a new Constellation project:

# Top-level configuration
name = "image-classifier-worker"
main = "src/index.ts"
node_compat = true
workers_dev = true
compatibility_date = "2023-05-14"
constellation = [
{binding = 'CLASSIFIER', project_id = '2193053a-af0a-40a6-b757-00fa73908ef6'},

​​ Install the client API library

In your image-classifier-worker Worker, install the client API library:

$ npm install @cloudflare/constellation --save-dev

​​ Upload model

Upload the pre-trained SqueezeNet 1.1 ONNX model to your image-classifier Constellation project:

$ wget
$ npx wrangler constellation model upload "image-classifier" "squeezenet11" squeezenet1_1.onnx
$ npx wrangler constellation model list "image-classifier"
id │ project_id │ name │
│ 297f3cda-5e55-33c0-8ffe-224876a76a39 │ 2193053a-af0a-40a6-b757-00fa73908ef6 │ squeezenet11 │

Take note of the id field as this will be the model ID.

​​ Download Imagenet classes

The SqueezeNet model was trained on top of the Imagenet dataset. Make a new src folder in your image-classifier-worker project directory. Then download the the list of 1,000 image classes that SqueezeNet was trained for:

$ mkdir src
$ wget -O src/imagenet.ts \

​​ Install modules

In your image-classifier-worker Worker, install pngjs, a PNG decoder, and string-to-stream:

$ npm install string-to-stream --save-dev
$ npm install pngjs --save-dev

​​ Code

With your Worker configured, begin coding in your image-classifier-worker’s index.ts file.

The following script gets a PNG file upload from the request, decodes the image to RGB raw bitmaps, constructs a 3D tensor with the input data, runs the SqueezeNet model, maps the top predictions to the ImagetNet human-readable classes and returns the strongest one in a JSON object.

Replace 297f3cda-5e55-33c0-8ffe-224876a76a39 with your actual model ID.

import str from "string-to-stream";
import { PNG } from "pngjs/browser";
import { imagenetClasses } from "./imagenet";
import { Tensor, run } from "@cloudflare/constellation";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const formData = await request.formData();
const file = formData.get("file") as unknown as File;
if (file) {
const data = await file.arrayBuffer();
const result = await processImage(env, data);
return new Response(JSON.stringify(result));
} else {
return new Response("nothing to see here");
async function processImage(env: Env, data: ArrayBuffer) {
let result;
const input = await decodeImage(data).catch((err) => {
result = err;
if (input) {
const tensorInput = new Tensor("float32", [1, 3, 224, 224], input);
const output = await run(
// Replace this with your actual model ID generated when you created your Constellation project
const predictions = output.squeezenet0_flatten0_reshape0.value;
const softmaxResult = softmax(predictions);
const results = topClasses(softmaxResult, 5);
result = results[0];
return result;
/* The model expects input images normalized in the same way, i.e. mini-batches of 3-channel RGB images
of shape (N x 3 x H x W), where N is the batch size, and H and W are expected to be 224. */
async function decodeImage(
buffer: ArrayBuffer,
width: number = 224,
height: number = 224
): Promise<any> {
return new Promise(async (ok, err) => {
// convert string to stream
const stream: any = str(buffer as unknown as string);
new PNG({
filterType: 4,
.on("parsed", function (this: any) {
if (this.width != width || this.height != height) {
err: `expected width to be ${width}x${height}, given ${this.width}x${this.height}`,
} else {
const [redArray, greenArray, blueArray] = new Array(
new Array<number>(),
new Array<number>(),
new Array<number>()
for (let i = 0; i <; i += 4) {
redArray.push([i] / 255.0);
greenArray.push([i + 1] / 255.0);
blueArray.push([i + 2] / 255.0);
// skip data[i + 3] to filter out the alpha channel
const transposedData = redArray
.on("error", function (error: any) {
err({ err: error.toString() });
// Refer to
// Transforms values to between 0 and 1
// The sum of all outputs generated by softmax is 1.
function softmax(resultArray: number[]): any {
const largestNumber = Math.max(...resultArray);
const sumOfExp = resultArray
.map((resultItem) => Math.exp(resultItem - largestNumber))
.reduce((prevNumber, currentNumber) => prevNumber + currentNumber);
return => {
return Math.exp(resultValue - largestNumber) / sumOfExp;
/* Get the top n classes from ImagetNet */
export function topClasses(classProbabilities: any, n = 5) {
const probabilities = ArrayBuffer.isView(classProbabilities)
: classProbabilities;
const sorted = probabilities
.map((prob: any, index: number) => [prob, index])
.sort((a: Array<number>, b: Array<number>) => {
return a[0] == b[0] ? 0 : a[0] > b[0] ? -1 : 1;
const top = sorted.slice(0, n).map((probIndex: Array<number>) => {
const iClass = imagenetClasses[probIndex[1]];
return {
id: iClass[0],
index: parseInt(probIndex[1].toString(), 10),
name: iClass[1].replace(/_/g, " "),
probability: probIndex[0],
return top;
export interface Env {

​​ Test your project

​​ Download test images

In your image-classifier-worker Worker, download some test 224x244 PNG images you can use for tests.

$ wget -O cat.png
$ wget -O house.png
$ wget -O mountain.png

​​ Run wrangler dev

Start a local server to test your image-classifier-worker Worker by running wrangler dev:

$ npx wrangler dev
⬣ Listening at

To classify some test images, run the following commands in your image-classifier-worker Worker:

$ curl -F file=@cat.png
{"id":"n02124075","index":285,"name":"Egyptian cat","probability":0.5356272459030151}
$ curl -F file=@house.png
$ curl -F file=@mountain.png

Your image classifier is ready. Run it through other 224x244 PNG images of your own and review the results.

​​ Deploy your project

When you are ready, deploy your Worker:

$ npx wrangler publish

Your image-classifier-worker Worker reads an image from a request, decodes it into a multidimensional float32 tensor, feeds it to the SqueezeNet model running in your Constellation project, gets the results, matches them with the ImageNet classes list, and returns the human-readable tags for the image. This project created in this guide only decodes PNGs, but Constellation supports other formats.