0xlgmz
GoFiberWeb AppsCreative Tools

proj-bannerbro

A quick social media banner generator built with Go Fiber, templates, JavaScript, and PostgreSQL after seeing a paid version of the same idea.

Role
Design and engineering
Status
Archived experiment
Published
GoFiberDjango TemplatesGORMPostgreSQLJavaScripthtml2canvasDockernginx

proj-bannerbro was a boredom project that turned into a small product experiment.

I had seen a service charging people to generate simple social media banners and wanted to understand how hard it would be to replicate the core idea myself.

The answer was: not very hard, at least for the first useful version.

BannerBro became a small Go web application that lets someone pick a social media banner format, customise the text, colours, font, and background image, then export the result as an image.

It was a practical way to build something visual, learn more Go Fiber, and break down a paid product into its core moving parts.

What It Does

The application provides banner generators for common social platforms:

  • Twitter / X
  • Facebook
  • YouTube
  • LinkedIn

Each banner type has its own dimensions stored in the database. The app renders a generator page for the selected platform and gives the user controls for:

  • Background colour.
  • Uploaded background image.
  • Background image position.
  • Background image size.
  • Font family.
  • Font colour.
  • Heading text.
  • Subheading text.
  • Bold heading and subheading toggles.
  • Optional BannerBro watermark.

The preview updates in the browser as the user changes the settings.

When the user clicks generate, the frontend clones the preview, scales it to the real banner dimensions, captures it with html2canvas, and downloads it as a JPG with FileSaver.js.

How It Was Built

The backend is written in Go using Fiber.

Fiber handles the routes, static files, and template rendering. The application uses the Django template engine for Go templates, with pages under application/views.

The main routes are intentionally simple:

  • / lists the available banner types.
  • /banner/:social renders the generator for a specific platform.
  • /banner/:social/desktop-content and /banner/:social/mobile-content were added while experimenting with responsive layouts.
  • /static serves the CSS, JavaScript, images, and frontend assets.

The data layer uses GORM with PostgreSQL.

On startup, the app connects to the database, runs auto-migrations, and seeds the supported banner dimensions:

  • twitter: 1500x500
  • facebook: 851x315
  • youtube: 2560x1440
  • linkedin: 1584x396

That made the banner formats data-driven instead of hardcoding each page by hand.

The Interesting Bit

The most interesting part of the project is that the actual image generation happens in the browser.

The Go backend does not render images server-side. It gives the browser the page, dimensions, and controls. JavaScript then builds the preview as HTML and CSS.

When it is time to download the banner, the app:

  1. Reads the selected banner dimensions.
  2. Clones the preview element.
  3. Scales the clone to the true platform size.
  4. Uses html2canvas to turn the DOM node into a canvas.
  5. Saves the canvas output as a JPG.
  6. Removes the cloned element from the DOM.

This was a good reminder that not every feature needs a heavy backend.

For this kind of tool, the browser already has enough capability to give the user a live preview and produce a downloadable image. The backend can stay focused on serving the app and tracking which banner formats are being used.

Analytics

The project also records basic banner analytics.

When a user opens a banner generator, the app records the banner type and a location value. If it is running behind Cloudflare, it reads CF-IPCountry. If not, it falls back to Gibraltar.

The database stores monthly banner analytics and individual click records. The generator page then shows the most used banner types ordered by click count.

This was not advanced analytics, but it gave the project a small product-like feedback loop:

  • Which banner types are people opening?
  • Which formats are most popular?
  • Could the homepage or sidebar be ordered by actual usage later?

For a small experiment, that was enough.

What I Learned

This project was useful because it forced me to think across the frontend and backend at the same time.

On the backend, I got more practice with:

  • Fiber routing.
  • Template rendering.
  • Static asset serving.
  • GORM models.
  • Database seeding.
  • Docker Compose with a Go app, Postgres, and nginx.
  • Environment-driven database configuration.

On the frontend, I got more practice with:

  • Live preview updates.
  • File upload handling with FileReader.
  • DOM cloning.
  • Canvas capture.
  • Browser-side downloads.
  • Responsive layout tradeoffs.

The core lesson was that a paid product is often just a clean wrapper around a small number of well-understood primitives.

That does not mean the product is worthless. Polish, reliability, UX, hosting, support, and distribution are real work.

But it does mean the core technical idea is often easier to test than it first appears.

Tradeoffs and Rough Edges

Looking back, there are a few things I would improve.

The browser-side generation is fast to build, but it has limitations. HTML-to-canvas rendering can be inconsistent depending on fonts, remote images, browser behaviour, and CSS support. A server-side renderer would give more control, but would also make the project heavier.

The responsive JavaScript was experimental and has some rough edges. The intent was to reshape the layout for smaller screens, but the implementation could be cleaner.

The analytics model is simple and useful for a prototype, but it does not separate unique visitors from repeated page views. If I wanted accurate usage analytics, I would design that more carefully.

The Docker setup was practical for local deployment, with Go, Postgres, and nginx in one Compose stack. For a production version, I would clean up the image build, remove development-only assumptions, and make the deployment path more deliberate.

The UI worked for the experiment, but a polished banner tool would need better preset templates, safer text scaling, better image cropping, and more export options.

What I Would Build Next

If I came back to BannerBro, I would add:

  • Preset layouts for each platform.
  • Drag-and-drop text positioning.
  • More export formats.
  • Server-side rendering for consistent output.
  • Template saving.
  • Better mobile editing.
  • Safer handling for custom fonts and uploaded images.
  • A cleaner analytics dashboard.
  • More platforms and aspect ratios.

The bigger version of this project would become less of a single-page generator and more of a lightweight creative tool.

Takeaway

proj-bannerbro started as boredom and curiosity.

I saw a small paid utility and wanted to understand how much of the experience I could rebuild myself. The result was a Go Fiber application that could render banner generators, let users customise them in the browser, and export the final image.

It was not a huge system, but it was a useful experiment.

It showed me that reverse-engineering the shape of a product is a good way to learn. You start with the user-facing outcome, work backwards through the moving parts, and then decide which parts are actually hard.

In this case, the hard part was not the first version.

The hard part would be turning it into something polished enough that people would choose it over the paid tool that inspired it.