DEV Community

Cover image for I Built a Generative Poster Tool ft. Vibe Code Arena
YASHWANTH REDDY K
YASHWANTH REDDY K

Posted on

I Built a Generative Poster Tool ft. Vibe Code Arena

There’s a very specific kind of disappointment that happens with generative design tools.

You click “Generate.”

Shapes appear. Colors shuffle. Layout changes.

Technically, it’s working.

But visually?

It looks like someone dropped geometry on the screen and walked away.

That’s the trap.

Because randomness alone doesn’t create design.

It creates noise.

And this challenge — building a “Generative Abstract Poster Creator” — quietly forces you to confront that.

The first version always lies to you

You start simple.

A loop:

for (let i = 0; i < 15; i++) {
  createRandomShape();
}
Enter fullscreen mode Exit fullscreen mode

Each shape gets:

  • random position
  • random size
  • random color

It feels productive.

Until you actually look at the result.

Everything overlaps awkwardly.
Spacing feels accidental.
Nothing aligns.

There’s no visual rhythm.

Just entropy.

The realization hits fast

Design is not randomness.

It’s controlled randomness inside constraints

And the moment you introduce constraints…

Everything changes.

The grid is the invisible system

The requirement says:

Snap shapes to a 4x4 or 8x8 grid

At first, that sounds like a layout detail.

It isn’t.

It’s the entire system.

Instead of placing shapes anywhere:

const x = Math.random() * width;
const y = Math.random() * height;
Enter fullscreen mode Exit fullscreen mode

You snap them:

const cell = 50;

const x = Math.floor(Math.random() * 8) * cell;
const y = Math.floor(Math.random() * 12) * cell;
Enter fullscreen mode Exit fullscreen mode

Now every shape aligns to an invisible structure.

And suddenly…

The output starts looking intentional.

This is where things get interesting

You haven’t changed:

  • number of shapes
  • types of shapes
  • randomness

You’ve only added constraint.

And yet the result feels like design.

Because human perception loves alignment.

Even when it’s not obvious.

Shapes are not equal

Another mistake shows up quickly.

You treat all shapes the same.

Circles, squares, triangles, lines.

Uniform probability.

But real compositions don’t work like that.

They have hierarchy.

Weight.

Balance.

So you start biasing:

const type = weightedRandom([
  "circle",
  "square",
  "triangle",
  "line"
]);
Enter fullscreen mode Exit fullscreen mode

Now some shapes appear more often.

Others become accents.

And the composition gains structure.

Size is where chaos sneaks back in

Even with a grid, random sizes can break everything.

A giant circle can overpower the layout.

Tiny shapes can disappear.

So you constrain size too:

const sizes = [1, 2, 3]; // grid units
const size = sizes[Math.floor(Math.random() * sizes.length)];
Enter fullscreen mode Exit fullscreen mode

Now everything scales relative to the grid.

No rogue elements.

The color palette is doing more than styling

Switching between:

  • Bauhaus
  • Forest
  • Cyber

Sounds like a theme toggle.

But it’s actually controlling contrast relationships.

If you randomly pick any color:

You get clashes.

If you pick from a curated palette:

You get harmony.

const palette = ["#ff0000", "#0000ff", "#ffd500"];
const color = palette[Math.floor(Math.random() * palette.length)];
Enter fullscreen mode Exit fullscreen mode

This tiny constraint removes 90% of bad outcomes.

Overlap is not a bug

It’s a feature — if handled correctly

When shapes overlap, things can look messy.

Unless you control how they blend.

This is where:

shape {
  mix-blend-mode: multiply;
}
Enter fullscreen mode Exit fullscreen mode

changes everything.

Now overlaps create new colors instead of visual clutter.

It feels intentional.

Almost printed.

Without this, your design looks like layers.

With it, it looks like composition.

The poster is not just a container

That 400x600 rectangle?

It defines proportion.

Which defines everything inside it.

If you don’t respect boundaries:

Shapes overflow.

Edges feel accidental.

So you enforce limits:

x = Math.min(x, width - shapeWidth);
y = Math.min(y, height - shapeHeight);
Enter fullscreen mode Exit fullscreen mode

Now nothing escapes.

Everything feels contained.

Containment is what makes it feel like a “poster”

Not just a canvas.

Hover interactions reveal something deeper

Adding a tooltip:

  • coordinates
  • dimensions

Feels like a small feature.

el.onmouseenter = () => showTooltip(x, y, w, h);
Enter fullscreen mode Exit fullscreen mode

But it exposes the system underneath.

Now users can see the grid

Even if it’s invisible.

And once they see it…

The randomness feels less random.

More deliberate.

The “Generate” button is misleading

It sounds like a trigger.

But it’s actually a reset.

Each click:

  • destroys the previous composition
  • creates a new one
  • reuses the same rules

That consistency is important.

Because if rules change between generations…

The system feels unstable.

What shows up in Vibe Code Arena

This challenge reveals something subtle.

Not who can generate shapes.

But who understands design systems

One model builds:

  • fully random placement
  • random colors
  • no constraints

It technically satisfies requirements.

But outputs feel chaotic.

Forgettable.

Another model introduces:

  • grid snapping
  • palette constraints
  • size normalization
  • blending modes

Now outputs feel… curated.

Even though they’re still random.

The human version usually leans into restraint

Not more features.

Not more randomness.

Just tighter rules.

Because good generative design is not about freedom.

It’s about guided variation

The strange shift that happens

At some point, you stop thinking:

“How do I generate more variety?”

And start thinking:

“How do I prevent bad outcomes?”

That’s when your system matures.

Because the goal isn’t infinite randomness.

It’s consistently good enough randomness.

The part you don’t expect

After building this, you start noticing something.

A lot of “design tools” are just:

  • randomness
  • with weak constraints

Which is why their outputs feel generic.

The difference between:

  • amateur generative design
  • and professional-looking output

Isn’t complexity.

It’s discipline in constraints

The shift

You stop seeing:

“random shapes on a poster”

And start seeing:

“a system generating compositions within a bounded design language”

That’s a very different mindset.

Because now you’re not designing outcomes.

You’re designing rules that produce outcomes

If you want to explore this properly

Don’t just build it once.

Tweak the constraints.

  • change grid density
  • limit color combinations
  • adjust size distribution

Watch how outputs evolve.

That’s where the real learning is.

Because this challenge looks like a UI generator.

But it’s actually about something deeper:

how structure creates beauty… even when randomness is involved

👉Try this exact challenge here: https://vibecodearena.ai/share/efdbbe8e-2b33-4e31-a15a-569ae1326b72

Top comments (0)