The problem with LinkedIn is it’s sort of a horrible necessity – a bit like going to visit the in-laws. That awful algorithm that prefers to reward trivial lifestyle updates over real substance really grates me. But a necessary evil, it is – there might be a client looking for exactly what you do, then again, it’s probably your peers looking for an idea to steal or an employee to rob. Clearly, then, the idea of producing any original IP for that platform is a waste of time. But having some visibility and a representation of your work and your company is a must.
If I must spend time on Linkedin I’m going to make it count, and for what it’s worth, carousels are probably the most visually impactful, attention-worthy way to go. So, I cobbled together a workflow (because of course I did) to take the pain out of production.

Here’s how it works: Gemini MCP generates SVG files from a text prompt, a Puppeteer script converts each one to PDF, and a four-line Python script merges them into a single file LinkedIn can read. The whole thing runs from the terminal and takes roughly ten minutes end-to-end once you’ve got the scripts set up.

What You’ll Need
- A Google AI Studio account (free tier works fine)
- Node.js installed (for Puppeteer)
- Python installed (for merging PDFs)
- A blog post or set of talking points to turn into slides
If you’ve got Claude Desktop with the Gemini MCP server, you can run this entire pipeline without leaving your terminal. Google AI Studio’s web interface works too, but you’ll be copy-pasting SVG output manually.
Why SVG, Not Images?
Most AI carousel tools generate raster images. Gemini can do that too, but the text rendering is unpredictable. Letters blur, spacing and fonts drift, and you’ll just end up fiddling with it in Canva anyway. Which defeats the whole point.
SVG sidesteps all of that. You’re generating structured markup, not pixels. Text renders as actual text, not a bitmap. Colours land on exact hex values (branding!). Every slide comes out identical in style because it’s code, not a rendered image.
There’s a less obvious advantage too: PDFs built from SVGs keep the text selectable, which LinkedIn’s algorithm, known for its thirst for quality, can index. Your readers can steal copy from it. And, there’s no pixelation on mobile either, because vectors scale to whatever viewport they land on.
Here’s how:
Step 1: Plan Your Slides
LinkedIn carousels work best between 8 and 12 slides. Try to make one point per slide. Aim for 25-50 words, max. Anything more than that will get crushed on mobile.
Here’s a decent structure:
- Cover slide – A bold question or statement that stops the scroll
- Problem slide – Why this matters, what’s broken
- Content slides (4-6) – One key point per slide
- Takeaway slide – Summary or “what I learned”
- CTA slide – Follow prompt, link to full article
If you’re repurposing a blog post, pull out the bits that stand up on their own. Most of what you wrote won’t survive the cut (my writing style can get a bit vague depending on the time of day). Try to distil your prose down to five or six usable lines from a 1,200-word post.
Step 2: Set Up Your Design System
Put the time into nailing down a colour palette and font stack (or do a good job of replicating from your brand guidelines). Then you can reuse your design system for every carousel until the inevitable rebrand project. This step is well worth the time upfront.
Example palette (all Tailwind stone/amber shades):
Background: #1c1917
Card: #292524 (with 24px border-radius)
Accent: #f59e0b (amber)
Title: #fafaf9 at 84px, weight 700
Body text: #a8a29e at 42px
Footer: #a8a29e at 28px, centred
Use these slide dimensions for LinkedIn: 1080 x 1350 pixels. That’s LinkedIn’s recommended 4:5 portrait ratio, and it fills most of the mobile screen, which matters because, according to the stats, that’s where 70%+ of LinkedIn browsing happens. 1080 x 1080 square works too, but portrait grabs more feed real estate.
Step 3: Generate SVG Slides with Gemini
One SVG per slide. Feed Gemini your design system and the slide content, and it gives you back clean markup.
A typical prompt looks like this:
Generate an SVG slide at 1080x1350 pixels with this design system:
- Background: #1c1917, card: #292524 with rx="24", 40px margin
- Accent colour: #f59e0b
- Title: #fafaf9, 84px, weight 700
- Body text: #a8a29e, 42px
Slide 3 of 8:
Title: "Your Text Stays Selectable"
Body: "Unlike raster images, SVG-generated PDFs keep
text as actual text. LinkedIn can parse it. Readers
can copy from it. And it never pixelates on mobile."
Footer: "houtini.com | 3/8"
What comes back is a valid SVG. Your text renders sharply, the colours should match exactly, and hopefully, each slide follows your design system because Gemini’s working from the same prompt skeleton each time.
One gotcha that cost me a bit of time: SVG has no automatic text wrapping. Anything past about 40 characters per line, and you’re doing the wrapping yourself. I use <tspan> elements. It’s manual but predictable:
<text x="110" y="640" class="body">
<tspan x="110" dy="0">Unlike raster images, SVG-generated</tspan>
<tspan x="110" dy="65">PDFs keep text as actual text.</tspan>
</text>
You could also use <foreignObject> to embed HTML inside the SVG and let CSS handle wrapping. I tried it. Puppeteer choked on the rendering in two out of three tests, so <tspan> it is.
Step 4: Convert SVGs to PDF
You can use Puppeteer to go from SVG to PDF, which, as I mentioned, keeps the text as vectors. Smaller files, selectable text, sharp at any zoom.
Save this as convert.js:
const puppeteer = require('puppeteer');
const path = require('path');
async function svgToPdf(svgFile, pdfFile) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`file://${path.resolve(svgFile)}`, {
waitUntil: 'networkidle0'
});
await page.pdf({
path: pdfFile,
width: '1080px',
height: '1350px',
printBackground: true,
pageRanges: '1'
});
await browser.close();
}
// Convert each slide
const slides = ['slide-01', 'slide-02', 'slide-03'];
for (const slide of slides) {
await svgToPdf(`${slide}.svg`, `${slide}.pdf`);
}
Run node convert.js and you get one PDF per slide.
That waitUntil: 'networkidle0' flag matters if your SVG pulls web fonts via @import. Without it, Puppeteer renders before the fonts load, and you get Arial where you expected Inter. Took me two batches of ugly slides to figure that one out.
Step 5: Merge into One PDF
LinkedIn wants one PDF, not eight, obviously. But with four lines of Python, pypdf you’re good: (pip install pypdf):
from pypdf import PdfWriter
merger = PdfWriter()
for i in range(1, 9):
merger.append(f"slide-{i:02d}.pdf")
merger.write("carousel.pdf")
merger.close()
The merged file needs to stay under 25MB, but with vector SVGs, you’ll land around 200KB–2MB for an 8-slide deck.
Step 6: Upload to LinkedIn
In the LinkedIn post composer, look for the document icon (page with a plus sign, weirdly hard to spot). Upload the PDF and give it a proper title, not “carousel.pdf.” That title sits at the top of every slide in the feed.
The text post pulls as much weight as the carousel itself. 500 characters max. I usually tease something from slide 4 or 5, so there’s a reason to swipe past the cover.

Quick Reference: Specifications
Quick reference if you’re tweaking any of this:
| Spec | Value |
|---|---|
| Format | PDF (organic posts) |
| Dimensions | 1080 x 1350px (4:5 portrait) |
| Alternative | 1080 x 1080px (square) |
| Max file size | 25MB total |
| Slide count | 8–12 ideal, 20 maximum |
| Min font size | 24pt for mobile |
Things I Got Wrong
Fonts
My first batch specified font-family: “Inter”, except I didn’t actually have Inter on my machine. Chrome fell back to Arial, and the spacing was completely off. The fix is either installing the font or adding a Google Font @import to the SVG’s <style> block. Chrome Headless fetches and renders it properly.
Wasting my life on ImageMagick
ImageMagick’s SVG renderer on Windows is awful. It just breaks: the CSS styling, it ignores gradients, and it mangles text alignment. Avoid it like the plague and just use Puppeteer, which uses Chrome’s actual rendering engine.
Too much stuff on one slide
I crammed 80 words onto each slide in my first batch. They rendered fine at 1440p but were a wall of grey mush on my phone. Cut everything to 30-40 words, and engagement roughly doubled. Turns out the 25-50 word rule exists because most people are reading on a 6-inch screen.
What I think I’ll do with this next
It’s full automation: feed a blog URL to Claude, extract the key points, generate SVG prompts automatically, pipe it all through these scripts. I’ve got a rough version working as a multi step agent prompt. It’s cool. And absolutely horrifying for the single page web app crowd.
Enjoy!
Related Articles
How to Create LinkedIn Carousel Slides with Gemini and Claude
A developer workflow for turning blog posts into LinkedIn carousel slides using Gemini SVG generation, Puppeteer PDF conversion, and a four-line Python merge script. No Canva, no SaaS tools.
What Is an MCP Server? And, Why It Matters for AI Tool Use
My daily work literally depends on the existence of MCP servers now, spread between Claude Desktop and Claude Code. Database queries, image generation, web scraping, file management, search console data, email. Much of my daily working world lives in a conversation window. I am convinced we are at the beginning of a radical shift in … <a title="How to Create LinkedIn Carousel Slides with Gemini and Claude" class="read-more" href="https://houtini.com/create-linkedin-carousel-slides-gemini/" aria-label="Read more about How to Create LinkedIn Carousel Slides with Gemini and Claude">Read more</a>
How to Improve Your AI Prototype Designs with Skills, Prompts and Gemini
I build a lot of single-file HTML prototypes with Claude Code. They work, but they all end up looking the same. I tested three approaches to fix this – Claude Skills, manual prompt engineering, and Gemini MCP feedback.
How to Cut Your Claude Code Bill by Offloading Work to Cheaper Models (with houtini-lm)
I built houtini-lm because I think there will be a time when your Anthropic bill will be getting a touch out of hand. In my experience, deals that seem a bit too good to be true do not last. Just this week I left Claude Code running a massive overnight refactor. I woke up, and, … <a title="How to Create LinkedIn Carousel Slides with Gemini and Claude" class="read-more" href="https://houtini.com/create-linkedin-carousel-slides-gemini/" aria-label="Read more about How to Create LinkedIn Carousel Slides with Gemini and Claude">Read more</a>
Claude Code: The Complete Beginner’s Guide
I’ve been running Claude Code every day for the last few months. MCP servers, articles across three sites, Python scripts, content workflows that run from research straight through to WordPress upload. Nothing’s changed how I work this much since I first picked up MS Excel 20 years ago. What in today’s posts is most of … <a title="How to Create LinkedIn Carousel Slides with Gemini and Claude" class="read-more" href="https://houtini.com/create-linkedin-carousel-slides-gemini/" aria-label="Read more about How to Create LinkedIn Carousel Slides with Gemini and Claude">Read more</a>
How to Run Free AI Text Detection Locally with Python and an NVIDIA GPU
I’ve been curious about AI content detection for a while. Not how to beat it – but how it works under the hood. Did you know “the best” model in the world is completely free, runs on any PC, and nobody seems to know about it? Everyone’s paying fifteen quid a month for Originality.ai when … <a title="How to Create LinkedIn Carousel Slides with Gemini and Claude" class="read-more" href="https://houtini.com/create-linkedin-carousel-slides-gemini/" aria-label="Read more about How to Create LinkedIn Carousel Slides with Gemini and Claude">Read more</a>