JavaScript Development Space

Creating a Next.js Image Editor with glfx.js

1 April 202519 min read
Build a Powerful Image Editor with Next.js and glfx.js

Modern web applications often require real-time image processing capabilities. With Next.js for performance and glfx.js for GPU-accelerated image effects, we can create a fast and responsive image editor with various filters and transformations.

Demo | Source Code

In this guide, we’ll cover:

  • Setting up a Next.js project
  • Integrating glfx.js for real-time image effects
  • Implementing file uploads
  • Adding filters and transformations
  • Optimizing performance

Let’s get started!

Step 1: Set Up a Next.js Project

First, create a new Next.js project:

npx create-next-app@latest next-image-editor

Install glfx.js for image processing:

npm install glfx

Initialize ShadCN UI

To set up ShadCN UI for a modern interface, install it with:

npx shadcn-ui@latest init

Then, install the necessary components:

npx shadcn-ui@latest add button select slider card label tabs

Step 2: Add glfx Hook and Types

Create lib/use-glfx.ts to handle loading glfx.js dynamically:

Loading code editor...

Create types/glfx.d.ts for type safety:

Loading code editor...

Create html2canvas.d.ts to work with the html2canvas library:

Loading code editor...

Step 3: Create the Image Editor Component

Now, create a component to handle image uploads and apply filters.

Create a new file: components/image-editor.tsx and add the following code:

Loading code editor...

Key Features

  • Uses Next.js and ShadCN UI for UI components (Select, Slider, Card, etc.).
  • Loads glfx.js dynamically using the useGlfx hook.
  • Applies real-time WebGL filters like brightness, contrast, sepia, vignette, etc.
  • Allows users to adjust filter parameters via sliders.
  • Supports saving edited images as PNGs using html2canvas.

Breakdown of Important Parts

1. Filter Configuration The filterConfigs object defines available filters and their parameters (range, default values).

2. State Management

  • selectedFilter stores the active filter.
  • filterParams holds parameter values for the selected filter.
  • texture and canvas handle WebGL rendering.
  • originalImage stores the uploaded image.

3. Image Initialization (useEffect)

When an image is uploaded, it:

  1. Loads into an <img> element.
  2. Creates a WebGL canvas using window.fx.canvas().
  3. Converts the image into a texture.
  4. Scales the image to fit the editor.
  5. Draws the texture to the canvas.

4. Applying Filters (useEffect)

  • When the user selects a filter or adjusts parameters, canvas.draw(texture) applies the filter dynamically.
  • Example: canvas.brightnessContrast(amount, 0); adjusts brightness.

5. Save Feature

Uses html2canvas to take a screenshot of the WebGL editor and save it as an image.

Step 4: Implement a Fallback Editor

Not all browsers support WebGL. If glfx.js fails to load, we can provide a fallback editor using the standard Canvas API.

Create components/fallback-editor.tsx and add:

Loading code editor...

FallbackEditor is a client-side React component that takes an imageUrl prop and lets users apply various filter effects to that image. It uses the native Canvas API rather than a specialized image editing library.

Technical Details

  • Uses React's useRef to access the Canvas DOM element
  • useEffect hooks handle image loading and filter reapplication when parameters change
  • TypeScript is being used (notice the HTMLCanvasElement and HTMLImageElement type annotations)
  • The component is marked with 'use client' directive, indicating it's meant to run on the client side in Next.js

This is a relatively simple implementation of an image editor that provides basic functionality without requiring external image processing libraries.

Step 5: Add a Script Loader

Create an components/script-loader.tsx file and add:

Loading code editor...

Your Next.js image editor depends on external JavaScript libraries like:

  • glfx.js (for WebGL image effects)
  • html2canvas (for converting elements into images)

However, these libraries:

✅ Are not built into Next.js and must be loaded dynamically.
✅ Use browser-specific features (like WebGL and the DOM), which won’t work on the server.
✅ May fail to load in certain environments, requiring a fallback solution.

How ScriptLoader Solves These Problems

1. Prevents Crashes in Non-WebGL Browsers

  • If WebGL is unsupported, useGlfx() sets glfxStatus to "error".
  • The fallback UI automatically replaces the WebGL editor with the standard canvas-based editor.

2. Dynamically Loads html2canvas When Needed

  • Instead of bundling html2canvas with your app, it is loaded only when required.
  • This reduces initial page load time and improves performance.

3. Ensures Scripts Are Fully Loaded Before Rendering

  • Prevents "html2canvas is not defined" errors by waiting until the script is ready.
  • Shows a loading message instead of a broken UI while scripts are loading.

What Happens Without ScriptLoader?

❌ The app might crash in browsers without WebGL.
❌ html2canvas could be undefined if used before loading.
❌ Users would see blank screens instead of a graceful fallback.

Step 6: Add a Debug Canvas

For debugging purposes, add a DebugCanvas component to visualize image loading and rendering issues.

Create components/debug-canvas.tsx and add:

Loading code editor...

This DebugCanvas component allows you to display an image on a canvas and provides the option to save the canvas as an image file. Here’s a breakdown of how it works:

1. Component Setup:

  • Props: The component accepts an imageUrl prop, which is the URL of the image that you want to load and display on the canvas.
  • Ref: It uses the useRef hook to create a reference (canvasRef) to the canvas element, which allows access to the canvas DOM node for drawing.

2. Effect Hook:

The useEffect hook is used to load and display the image on the canvas when the imageUrl changes.

3. Image Save Functionality:

  • The handleSaveImage function allows you to save the content of the canvas as a PNG image.
  • It creates a temporary <a> link with a download attribute and sets the href to the data URL of the canvas (which is a base64-encoded PNG of the canvas content).
  • The link is programmatically clicked, triggering the download, and then the link is removed from the document.

This component is useful for debugging image loading and manipulation in the browser. It helps you see the exact content of an image on the canvas and provides a way to save that content.

Step 7: Adding Layout to the Article:

Below is a refined breakdown of your code and how it fits into your article. This layout acts as a global structure around your app, so every page or component rendered as children will inherit the layout style:

Loading code editor...

Script Loading:

The Script component is used to include the glfx.js library, which is critical for GPU-accelerated image editing. The strategy="afterInteractive" ensures the script loads only after the page becomes interactive, which improves performance.

This RootLayout would be used as the wrapper for your image editor app, ensuring consistent layout and styling across all pages in your app. The content of your article (or any other page) would be rendered as the children prop, inheriting the layout's properties.

Step 8: Add the Editor to the Page

Now, modify page.tsx to include the image editor. To integrate everything into page.tsx and ensure the image editor works correctly with dynamic components like the ImageEditor, FallbackEditor, and ScriptLoader, here’s a step-by-step explanation of the code:

Loading code editor...

Key Features:

1. File Selection:

The handleFileChange function manages image selection. Once a file is chosen, it creates an object URL and stores it in imageUrl. The URL is revoked after the component unmounts or the image changes, ensuring that no unused URLs are lingering in memory.

2. Dynamic Imports:

  • The ImageEditor and ScriptLoader components are dynamically imported with ssr: false, ensuring they are only loaded on the client-side. This is critical for components that depend on browser-specific APIs (like WebGL).

  • If ImageEditor is not loaded yet, the ScriptLoader will show a fallback editor (in this case, FallbackEditor).

3. Tabs:

  • The app allows switching between two image editors—Standard Editor and WebGL Editor—via tabs. This is implemented with the Tabs component, where each tab displays a different editor.
  • If WebGL isn't supported or fails to load, the fallback editor will be displayed.

4. Image Upload:

If no image is selected (!imageUrl), a file input is shown that allows users to upload an image.

How It All Connects:

1. Page Flow:

  • The page initially asks the user to upload an image.
  • Once the image is uploaded, the user can toggle between two types of editors (standard and WebGL) using tabs.
  • The ScriptLoader dynamically loads the WebGL editor, which is only available after the script (glfx.js) is loaded.
  • If WebGL fails, a fallback editor is shown.

2. User Experience:

The UI ensures a smooth experience where the user can easily upload, edit, and switch between different editors.

This setup efficiently manages dynamic loading, browser-specific behavior, and image manipulation in a React app built with Next.js.

Conclusion

The Image Editor with glfx.js provides a powerful, browser-based solution for image manipulation, combining the simplicity of standard image editing with the advanced capabilities of WebGL. By leveraging the power of glfx.js for GPU-accelerated filters and effects, users can create stunning edits in real-time. With a smooth and user-friendly interface built using Next.js and TailwindCSS, this application ensures a seamless experience for both novice and advanced users.

Whether you're working on basic image enhancements or experimenting with creative WebGL-powered filters, this editor offers flexibility and performance. Additionally, the fallback editor ensures compatibility across a wide range of devices, ensuring your image editing experience remains robust and reliable.

Try it out today, and explore the endless possibilities for image manipulation directly in your browser!

JavaScript Development Space

JSDev Space – Your go-to hub for JavaScript development. Explore expert guides, best practices, and the latest trends in web development, React, Node.js, and more. Stay ahead with cutting-edge tutorials, tools, and insights for modern JS developers. 🚀

Join our growing community of developers! Follow us on social media for updates, coding tips, and exclusive content. Stay connected and level up your JavaScript skills with us! 🔥

© 2025 JavaScript Development Space - Master JS and NodeJS. All rights reserved.