Janarayson

Marketing as code, part 2: posts and carousels

Engineering9 min read

The WhatsApp simulator from part 1 now makes our Instagram carousels too, with photos generated from prompts and a Claude Code skill that writes most of each carousel for me.

In part 1, I built a WhatsApp simulator so we could rehearse Mama’s chats and turn them into reels.

Reels are only part of what goes on our Instagram, though. We also post single images and carousels, and a lot of them need a picture of Mama in WhatsApp too.

Before this, a picture like that meant capturing a screenshot, or making edits in a Figma file and exporting it. Now I don’t need either. Since I’m the solo dev, I ended up using the simulator for much more than reels, and code makes this super simple and fast.

This is one of the carousels it makes, a payday reset in eight slides.

  • Slide 1: a man at a kopitiam window with teh tarik, headline Salary in. Gone by the 10th.1. Hook
  • Slide 2 on coral: It's not discipline. It's the order money leaves.2. Insight
  • Slide 3 on coral: Claim your fixed commitments first.3. Step 1
  • Slide 4 on coral: Pull out your debt payment now.4. Step 2
  • Slide 5 on coral: Pay yourself first.5. Step 3
  • Slide 6 on coral: Split what's left into weekly caps.6. Step 4
  • Slide 7: a WhatsApp chat where Mama records grab dinner rm32 against a RM300 weekly cap, with the caption Step 5: track it in WhatsApp.7. Step 5
  • Slide 8: a woman with a cup of coffee on a balcony at sunset, a moneymama.co pill and the headline One calm ritual on payday. Let Mama hold the rest.8. CTA
The payday reset carousel, as exported. Tap a slide to see it full size.

Freezing the simulator

In carousel mode, the same WhatsApp screen from the reels stops moving. Each slide freezes a chat at one point, with the animations switched off so the image comes out the same every time.

A slide can be a chat, a page in the browser, a text slide with a big headline, or a photo, and there’s an intro and an outro in our colours. Each one is 1080×1350 for Instagram’s 4:5 posts, or 1080×1080 for squares.

Export All draws every slide in the browser, captures each one as a PNG and gives me a zip, ready to post.

The carousel engine showing slide 7 of the payday reset: a WhatsApp chat with Mama recording a RM32 dinner against a weekly cap, on a coral background with tiled MoneyMama logos, next to a panel with the format, carousel, slide theme, swipe arrow and the list of slides
The carousel engine, on the chat slide of the payday reset.

Changing a carousel means changing a file. If Mama’s reply needs a different amount, I change the text and export again.

Photos from prompts

In June we started turning our blog posts into educational carousels: the payday reset, why your card balance barely moves, emergency funds, the M40 squeeze and our seven-step Money-It-Right framework.

Those needed photos as well as chats.

So a photo slide carries its own prompt. When I select one, the tool shows the prompt with two buttons: Copy prompt, to paste into ChatGPT for free, or Generate, which sends it to OpenAI’s gpt-image-2 and saves the photo where the slide expects it. I used both.

The carousel engine's sidebar with the payday reset's eight slides listed and the first, hook, selected, and below it an Image prompt panel showing the start of the prompt, with Copy prompt, Generate and Copy filename buttons and the file it saves to
The slide list and the prompt panel, with the payday reset's first slide selected.

Every prompt follows the same house style: candid Malaysian lifestyle, natural side light, 35mm, a matte finish with fine grain, a touch of our coral or yellow, and room at the bottom for the headline. A few things are banned outright: piggy banks, coins, charts, and any text or logos in the photo.

This is the prompt from that panel, in full:

Cinematic candid portrait of a young Malaysian man in his late twenties at a kopitiam in the early evening, shot from outside through the window glass so faint street reflections (passing motorbikes, a blur of people, soft neon glow) drift across the frame and over him. He sits with a glass of teh tarik, gazing out past the camera in a quiet, far-off payday moment, a little wistful, not distressed. Warm golden-hour light rakes in from the side, moody cinematic color grade, shallow depth of field, 50mm, matte finish, fine film grain, gentle lens flare. Muted warm palette on #FEFDFC tones with one subtle #FF4F40 accent (a sign, a cup, a chair). Authentic Malaysian kopitiam, marble table, mosaic-tiled floor. Keep the lower third calm and uncluttered for a caption. No readable text or signage, no logos, no charts, no coins. Cinematic editorial, not stock-corporate.

And this one is for the card balance carousel, a still life instead of a person:

Conceptual editorial still-life photograph, no people, no text, no logos. A classic glass hourglass standing on a warm cream tabletop, almost all of its fine sand still resting in the top bulb with only a thin trickle fallen through, suggesting a lot of time passing but barely any progress. A single plain matte credit card (no readable text or numbers) lies flat on the surface nearby. Bright high-key airy natural side light from the left, soft long shadows, shallow depth of field, 35mm, matte finish, fine film grain. Clean #FEFDFC cream canvas dominating the frame with generous negative space at the bottom for a headline; one gentle #FF4F40 coral accent (a folded linen napkin) and a small hint of #F8E15D. Calm, premium, editorial, not stock-corporate. No coins, no charts, no pigs.
A glass hourglass with most of its sand still in the top bulb, a plain credit card and a coral linen napkin on a cream table in soft side light
What came back for the card balance carousel, before the headline goes on.

The lifestyle photos on our new website were made the same way.

A young man in a peach baju at a kitchen counter, smiling at his phone beside a cup of teaA woman in a floral dress relaxing on a sofa, smiling at her phone, with a teacup on the tableA woman in a hijab on a sofa with batik cushions, smiling at her phone under a warm lamp
Three of the lifestyle photos on moneymama.co.

Writing the slides and the prompts by hand still took time, so I wrote a skill for it.

A skill in Claude Code is a markdown file of instructions. It has a name and a description, and Claude Code loads it when a task matches that description. Mine is called mama-ig, and it picks up on things like “make a carousel” or “/mama-ig”.

Give it a topic or one of our blog posts, and it reads the post from disk, proposes the slides and waits for my OK. Then it writes the carousel file, the photo prompts in the house style and the Instagram caption.

The file is laid out in sections. It covers the only correct paths in the repo, the workflow, the carousel’s data model with an example, the house style for photos, the rules for Mama’s voice, the caption format, the common mistakes, and a checklist at the end. For the brand voice, it builds on another skill of mine, mama-blog, and says that when the two disagree, mama-blog wins.

A few of the rules in it:

  • “You write content and code; you never fetch or invent images, stats, or handles.” The skill writes; I generate the photos, and nothing gets made up.
  • Never name the negative feeling. Words like “shame” and “guilt” are banned from slides and captions, because “saying ‘no shame here’ plants the exact word you’re trying to lift.” It describes a moment instead, like the bill notification you swipe away.
  • The caption is Mama talking, in the first person, like “I made you this one because I kept thinking about you.”
  • Read the blog post from disk, because “session-compressed copies drop words and mangle the voice.”

This is the checklist it ends with:

- [ ] Source read from disk (if adapting a blog post); structure approved
- [ ] `src/carousel/data/<id>.ts` written + registered in `index.ts`; `npx tsc --noEmit` clean
- [ ] ≤10 slides, 4:5; voice on-brand; no em-dashes; no named feelings (shame/guilt/judgement); light Manglish
- [ ] Caption in Mama's first-person voice ("I"/"me", reader as "you"), not a neutral brand narrator
- [ ] Caption hook opens on a felt moment; heart-led, shows not tells; reader feels seen not lectured
- [ ] Photo prompt(s) in house style; exact `public/img/generated/<carouselId>-<slideId>.png` name(s) given
- [ ] Caption: hook in first 125 chars, swipe-led, collab credited (+ IG handle check), WhatsApp/moneymama.co CTA, save line, hashtags
- [ ] Real sources/handles only; nothing fabricated

Skills for specific tasks

I write custom skills like this for specific tasks, to make things easy. mama-blog writes our blog posts and the prompts for their cover photos, and mama-ig turns those posts into carousels.

I think we should start embracing AI tools more, and for automation too.

All five carousels are scheduled on our Instagram, next to the reels.

Update, September 2026: the blog itself, and how its covers stopped all looking alike, is part 3.