Build an AI Meme Generator 🤖
Real "AI" is expensive. Randomness is free. Many "AI" entertainment tools actually use Procedural Generation. They have a massive list of "Structures" and randomly fill in the blanks.
- Structure: "When you [verb] the [noun]..."
- Fill: "When you [delete] the [production database]..."
In this guide, we will build a "Meme Bot" that feels intelligent but runs entirely in the browser.
Step 1: The "Brain" (Data Structures) 🧠
Instead of an OpenAI API key, we will creating a "Meme Logic" engine.
const memeTemplates = [
{
name: "Drake Hotline Bling",
url: "/memes/drake.jpg",
structures: [
{ top: "Using [tech1]", bottom: "Using [tech2]" },
{ top: "[bad_action]", bottom: "[good_action]" }
]
},
{
name: "Distracted Boyfriend",
url: "/memes/distracted.jpg",
structures: [
{ top: "Me", bottom: "[new_framework] | [old_framework]" }
]
}
]
const vocabulary = {
tech1: ["jQuery", "PHP", "Notepad"],
tech2: ["React", "Next.js", "VS Code"],
bad_action: ["Testing in Production", "Writing Docs"],
good_action: ["Pushing to Master", "Coding blind"]
}
Step 2: The Logic (Randomizer) 🎲
We write a function that picks a template and "hydrates" it with vocabulary.
function generateMeme() {
// 1. Pick random template
const template = memeTemplates[Math.floor(Math.random() * memeTemplates.length)];
// 2. Pick random specific caption structure for that image
let caption = template.structures[Math.floor(Math.random() * template.structures.length)];
// 3. Replace placeholders like [tech1]
const fill = (text) => text.replace(/\[(.*?)\]/g, (match, key) => {
const words = vocabulary[key];
return words ? words[Math.floor(Math.random() * words.length)] : match;
});
return {
image: template.url,
topIdx: fill(caption.top),
bottomIdx: fill(caption.bottom)
};
}
Step 3: The React UI 💻
Here is the component. We can just use standard HTML/CSS absolute positioning to overlay the text. No heavy Canvas libraries needed!
"use client"
import { useState } from "react"
import { Sparkles, RefreshCw } from "lucide-react"
export default function MemeBot() {
const [meme, setMeme] = useState<{image: string, top: string, bottom: string} | null>(null)
// Minimal vocabulary for the example
const tech = ["Java", "Python", "Rust", "JavaScript"]
const feelings = ["Pain", "Joy", "Confusion", "Hunger"]
const hitMe = () => {
// Simulate "Thinking..."
setMeme(null);
setTimeout(() => {
const t = tech[Math.floor(Math.random() * tech.length)]
const f = feelings[Math.floor(Math.random() * feelings.length)]
setMeme({
image: "https://i.imgflip.com/1bij.jpg", // Batman slapping Robin
top: `I love coding in ${t}!`,
bottom: `Shut up and feel the ${f}!`
})
}, 800)
}
return (
<div className="max-w-md mx-auto p-4">
<button
onClick={hitMe}
className="w-full bg-gradient-to-r from-purple-600 to-pink-600 text-white font-black py-4 rounded-xl shadow-xl flex items-center justify-center gap-2 hover:scale-105 transition"
>
<Sparkles size={20} /> GENERATE MEME
</button>
{meme ? (
<div className="relative mt-6 rounded-lg overflow-hidden shadow-2xl border-4 border-black group">
<img src={meme.image} className="w-full object-cover" alt="Meme" />
{/* Standard Meme Text Styling */}
<h2 className="absolute top-2 left-0 w-full text-center text-white font-[Impact] text-3xl uppercase tracking-wide px-2 drop-shadow-[0_2px_0_#000] stroke-black text-shadow-meme">
{meme.top}
</h2>
<h2 className="absolute bottom-2 left-0 w-full text-center text-white font-[Impact] text-3xl uppercase tracking-wide px-2 drop-shadow-[0_2px_0_#000] stroke-black text-shadow-meme">
{meme.bottom}
</h2>
</div>
) : (
<div className="mt-8 text-center text-slate-400 py-12 border-2 border-dashed rounded-xl">
<RefreshCw className="mx-auto mb-2 animate-spin" />
Thinking of a joke...
</div>
)}
</div>
)
}
Step 4: The Secret Sauce (CSS Text Stroke) 🎨
To make text look like a REAL meme, you need that classic white-text-with-black-outline look.
CSS text-shadow is good, but -webkit-text-stroke is better.
/* Add this to your global.css or module */
.text-shadow-meme {
text-shadow:
-2px -2px 0 #000,
2px -2px 0 #000,
-2px 2px 0 #000,
2px 2px 0 #000;
}
This ensures the text is readable on ANY background color!