DEV Community

Cover image for Building a Free Instagram Editor with Svelte 5, WASM, & Llama 3.1
Aribu js
Aribu js

Posted on • Originally published at shcho-i-yak.pp.ua

Building a Free Instagram Editor with Svelte 5, WASM, & Llama 3.1

Hello DEV community! πŸ‘‹ Building a web-based graphic editor usually means wrestling with HTML5 Canvas or heavy WebGL libraries. But what if you could bypass Canvas entirely, manipulate the DOM directly with pure CSS, wrap it in blazing-fast Svelte 5 runes, and inject local WASM for background removal?

Today, I want to share the technical journey of building SMM Turbo - a completely free, in-browser Instagram carousel editor. We'll look "under the hood" at SvelteKit state management, local AI image processing, Llama 3.1 integration via Groq API, and why I chose the DOM over Canvas for rendering. Let's dive in! πŸš€

πŸ— Architecture: DOM instead of Canvas

If you've ever been interested in developing graphic editors, you know that the standard approach is to use HTML5 <canvas> or special libraries like Fabric.js or Konva. But I took a different, somewhat rebellious path 🐧.

In SMM Turbo, the working area is ordinary HTML elements (<div>, <span>, <img>) that are positioned absolutely using x and y coordinates.

Why so? Because CSS is a superpower! πŸ¦Έβ€β™‚οΈ

Rendering through DOM allowed me to implement incredibly complex typography in just a few lines of code, instead of writing complex math for drawing pixels on Canvas.

Here's what regular CSS gives you:

  • Gradient text: Using -webkit-background-clip: text.
  • Stroke (outline): Works through -webkit-text-stroke.
  • Shadows and glow: Elementary drop-shadow and text-shadow.
  • Text bending along a circle: Implemented by breaking the text into separate <span> elements using transform: rotate(...) translateY(...).

But how do you save the finished picture then? That's where the html-to-image library comes in.

Here's what my script for exporting to +page.svelte looks like, which takes a "screenshot" of the DOM node, packs the slides into an archive using JSZip, and converts them "on the fly":

const options = { 
    width: editor.canvasWidth, 
    height: editor.canvasHeight, 
    style: { transform: 'scale(1)', transformOrigin: 'top left' }, 
    pixelRatio: 1, 
    cacheBust: true 
};

// Draw Canvas from DOM
const canvas = await toCanvas(node, options);
// Save in the selected format (WebP/JPEG) with the desired quality
const dataUrl = canvas.toDataURL(editor.exportFormat, qualityDecimal);

capturedImages.push(dataUrl);

// Pack all slides into a ZIP archive
const zip = new JSZip();
capturedImages.forEach((img, i) => {
    const base64Data = img.split(',')[1];
    zip.file(`smm-slide-${i + 1}.webp`, base64Data, {base64: true});
});
const content = await zip.generateAsync({type: "blob"});
Enter fullscreen mode Exit fullscreen mode

Thanks to this, we get perfect text quality, flexibility of CSS filters, and fast export of the entire carousel.

SMM Turbo editor interface with tools

🌍 Multilingualism with Runes: On-the-Fly Localization

As the project expanded, there was a need to add an English version of the interface and landing page to attract an international audience. In the React or Vue ecosystem, you'd typically install heavy libraries like i18next for this. But in Svelte 5, we can do it much more elegantly thanks to "runes".

I created a simple localization service based on the reactive $state, which instantly repaints the entire interface when the language changes, without resetting any unsaved changes on the carousel canvas:

// src/lib/i18n/i18n.svelte.ts
class I18nService {
    locale = $state('uk'); // Reactive language state

    t(key: string) {
        return translations[this.locale][key] || key;
    }

    setLocale(newLocale: string) {
        this.locale = newLocale;
        localStorage.setItem('smm_lang', newLocale);
    }
}
export const i18n = new I18nService();
Enter fullscreen mode Exit fullscreen mode

Because locale is a $state, any component using i18n.t('key') will instantly update as soon as the language changes. This also made it painless to set up automatic language detection: if a person enters the editor from an English or German browser, the script immediately reads navigator.language and automatically turns on the English version of the interface.

Svelte 5 localization service code

πŸͺ„ AI Magic: In-Browser background removal

A cool graphic editor for creating Instagram carousels in 2026 can't exist without built-in artificial intelligence.

Usually, services ask for money for background removal, because they send your photo to remote powerful servers with GPU. I decided to make this process local and free!

I used the incredible @imgly/background-removal library. This is a real revolution! The neural network is loaded in WebAssembly (WASM) format directly into your browser.

async function handleRemoveBackground() {
    isRemovingBg = true;
    try {
        const imgly = await import('@imgly/background-removal');
        const removeBg = imgly.removeBackground;

        // AI works directly on the user's device
        const imageBlob = await removeBg(selected.content);
        const url = URL.createObjectURL(imageBlob);

        selected.content = url;
        editor.saveState();
    } catch (error) {
        alert("Failed to remove background.");
    } finally {
        isRemovingBg = false;
    }
}
Enter fullscreen mode Exit fullscreen mode

You just select a photo, click the "Remove Background" button, and your computer perfectly cuts out the main object in a couple of seconds. No limits, subscriptions, or transferring confidential photos to third-party servers.

πŸ€– AI Co-Pilot based on Llama 3.1

In addition to working with images, I added a text AI assistant. A special purple button with a star ✨ at the bottom of the screen invokes the AI Co-Pilot panel. It works through the ultra-fast API from Groq.

To make the AI respond instantly and always in the correct format, I created a separate API route on the server (+server.ts). Here's the real backend code that's responsible for the magic of generating texts:

import { json } from '@sveltejs/kit';
import Groq from 'groq-sdk';
import { GROQ_API_KEY } from '$env/static/private';

const groq = new Groq({ apiKey: GROQ_API_KEY });

export async function POST({ request }) {
    const { prompt, mode } = await request.json();
    let systemMessage = "";

    if (mode === 'canvas') {
        systemMessage = `You are a creative copywriter. 
The user will ask you to come up with text. Provide exactly 3 short and catchy options. 
Format your response so that each option starts with a new line and the symbol πŸ”Ή. 
No introductory words! Language: English.`;
    } 
    // ... other modes (Alchemist, Artist)

    const chatCompletion = await groq.chat.completions.create({
        messages: [
            { role: 'system', content: systemMessage },
            { role: 'user', content: prompt }
        ],
        model: 'llama-3.1-8b-instant',
        temperature: 0.7,
        max_tokens: 1024,
    });

    return json({ result: chatCompletion.choices[0]?.message?.content });
}
Enter fullscreen mode Exit fullscreen mode

Here, I implemented 3 modes:

  1. πŸ§™β€β™‚οΈ Alchemist: Transforms a short idea into a detailed professional prompt for ChatGPT or Claude.
  2. 🎨 Artist: Helps generate detailed English-language requests for Midjourney.
  3. πŸ’¬ On canvas: The coolest feature! You ask to come up with a clickbait headline, AI gives you 3 options, and you can add the selected text directly to the current slide of the carousel with one click (+ On slide).

AI Co-Pilot in action within the editor

πŸ”„ State Management: Svelte 5 Magic

One of the biggest challenges in developing a graphic editor is state management (State Management). We need to store the position of each element, manage the active slide, and implement the undo/redo function.

Thanks to the new runes in Svelte 5 ($state), creating such a global storage has become incredibly elegant. I created a state.svelte.ts file, where I described the editor object:

function createEditorState() {
    let state = $state({
        mode: 'carousel',
        canvasFormat: '1:1',
        currentSlides: [{ id: Date.now().toString(), elements: [], backgroundOverlay: 0 }],
        activeSlideIndex: 0,
        selectedId: null,
        history: [],
        historyIndex: -1,

        get activeSlide() {
            return state.currentSlides[state.activeSlideIndex];
        },

        saveState() {
            // Logic for saving steps for Undo/Redo
            if (state.historyIndex < state.history.length - 1) {
                state.history = state.history.slice(0, state.historyIndex + 1);
            }
            state.history.push(JSON.stringify(state.currentSlides));
            state.historyIndex++;
        },

        undo() {
            if (state.historyIndex > 0) {
                state.historyIndex--;
                state.currentSlides = JSON.parse(state.history[state.historyIndex]);
            }
        }
    });
    return state;
}
export const editor = createEditorState();
Enter fullscreen mode Exit fullscreen mode

Every time an element's coordinates change (Drag & Drop), editor.saveState() is called, which adds a new snapshot of the data to the history array. Now the user can safely press Ctrl+Z, and the system will instantly revert the changes.

πŸ“¦ Storage and Integration with Stocks

So that users don't lose their masterpieces after closing the tab, I connected a backend based on Supabase (PostgreSQL + Auth + Storage).

Authorization takes a few seconds. After that, all your projects are automatically saved to the cloud database. To upload your own images, I use Supabase Storage, generating unique file names so they don't overwrite each other.

I also integrated the free photo stock Pixabay. The user can directly in the editor enter a search query in English (e.g., dark space, aesthetic coffee), get a grid of results through the Pixabay API, and with one click set the image as the slide background or add it as a separate element.

🏁 Conclusion

Creating your own technical product is always a challenge, but it's an incredible experience. SMM Turbo grew from a simple idea for personal needs into a full-fledged graphic editor for creating Instagram carousels, capable of closing 90% of a marketer's or developer's daily tasks.

A lightning-fast interface on Svelte 5, powerful artificial intelligence "under the hood", seamless background removal, and complete freedom of action (without annoying watermarks or limits) make it a great alternative to paid monopolists.

πŸ”₯ Want to try this magic for yourself? My editor is completely free and already available for testing.

πŸš€ Try SMM Turbo right now

No subscriptions, payments, or hidden fees. Built for creators.
πŸ‘‰ Open SMM Turbo Editor (Free)


Top comments (0)