Processing assets with Node.js

Processing assets with Node.js

Node.js was a great help in the project Heavens of Mankind to reduce the size of data files, and allow type-checking debugging, and processing data (you can read about it more in detail here).

We decided to use it again for our agency’s portfolio, to process images. This is usually not necessary, since the frameworks that we use offer plugins to process images and improve the performance of the website. However, this was made to cater to specific design requirements, since we need to display different images based on the user’s screen size. At first, we experimented with the Picture element, provided by Astro, but it seems that this element had too many constraints, like needing a constant aspect ratio.

import fs from "fs";
import sharp from "sharp";

const sourceFolderRoot = "./node/images/";
const basePath = "./public/";

interface ResizeImageProps {
  path: string;
  width?: number;
  height?: number;
}

const imagesFolders: ResizeImageProps[] = [
  { path: "htmlBackground/", width: 1920 },
  { path: "htmlHalfScreen/", width: 800 },
  { path: "threeJSButton/", width: 800 },
  { path: "threeJSMap/", width: 1024 },
  { path: "threeJSSprite/", width: 512 },
];

const getFiles = async (directoryPath: string) => {
  try {
    const files = await fs.promises.readdir(directoryPath);
    return files;
  } catch (error) {
    console.log(error);
  }
};

const resizeImage = async ({ path, width, height }: ResizeImageProps) => {
  try {
    await sharp(`${sourceFolderRoot}${path}`)
      .resize({
        width,
        height,
      })
      .jpeg({ progressive: true, force: false })
      .png({ progressive: true, force: false })
      .toBuffer(function (_, buffer) {
        fs.writeFile(`${basePath}${path}`, buffer, function (e) {
          if (e) {
            console.log(e);
          }
        });
      });
  } catch (error) {
    console.log(error);
  }
};

const compressImages = async ({ path, width, height }: ResizeImageProps) => {
  try {
    const files = await getFiles(`${sourceFolderRoot}${path}`);
    if (files) {
      files.forEach((filePath: string) => {
        resizeImage({
          path: `${path}${filePath}`,
          width,
          height,
        });
      });
      console.log("new files were successfully created");
    } else {
      console.log("files are undefined");
    }
  } catch (err) {
    console.log(err);
  }
};

imagesFolders.forEach((folder) => {
  compressImages(folder);
});

After using a node folder to process images for the Figures portfolio, I decided to also use it for other websites, where it can help accelerate our workflow. I therefore used it for our project for RSB.

The advantage of having an automatic way to process images is that this is needed for a lot of projects, so once it’s put in place it’s great to be able to reuse it.

It would also be possible to improve the project Heavens of Mankind with this method, since it currently uses Node.js only to process data, but not to process images. Indeed, the images used in the constellation’s texture are optimized manually (using tools like TinyPNG). They can not be optimized by Gatsby directly, since they need to be in the static folder to be used in the 3D scene. By using Node.js it’s not only possible to compress them, but also to resize them. So it would be possible to save the texture in a very high resolution and then choose the final size based on how big the constellation is in the scene (which could either be a setting entered manually or by using existing data, like how much the constellation needs to be zoomed in on smartphone).

I would still like to improve my knowledge and workflow with Node.js. At the moment, using it to code the same way as it’s done in the front end is difficult. I found a package, for Heavens of Mankind which allows me to use TypeScript and ES Modules inside of Node.js, which means I can also use constants that are used in the front end of the website. However, this package’s author does not maintain it, and it broke with the update of Node.js. That means that I need to use Node Version Manager to switch to an older version of Node.js every time I want to execute the code. In other projects, I found another package to use TypeScript with Node.js, but it also seems to have issues with updates.