TikTok App Marketing
Automate your entire TikTok slideshow marketing pipeline: generate → overlay → post → track → iterate.
Proven results: 7 million views on the viral X article, 1M+ TikTok views, $670/month MRR — all from an AI agent running on an old gaming PC.
Overview
A closed-loop marketing pipeline for an app or product on TikTok and Instagram, built on the Larry methodology: study what competitors in the category are already winning with, generate slideshow images, overlay hook text, post to both platforms, then read the numbers and let them pick the next batch's hooks.
The loop is the product. Generating slideshows is the easy half; measuring which hook drove installs and feeding that back is what makes it compound.
Prerequisites
This skill does NOT bundle any dependencies. Your AI agent will need to research and install the following based on your setup. Tell your agent what you're working with and it will figure out the rest.
Required
- Node.js (v18+) — all scripts run on Node. Your agent should verify this is installed and install it if not.
- node-canvas (
npm install canvas) — used for adding text overlays to slide images. This is a native module that may need build tools (Python, make, C++ compiler) on some systems. Your agent should research the install requirements for your OS. - Upload-Post — this is the backbone of the whole system. Upload-Post handles posting to TikTok, Instagram, and 10+ other platforms simultaneously with a single API call. It also provides analytics (followers, impressions, reach) and upload history (per-post tracking) that power the daily feedback loop. Without Upload-Post, the agent can't post or track what's working — and the feedback loop is what makes this skill actually grow your account instead of just posting blindly. Sign up at upload-post.com.
Image Generation (pick one)
You choose what generates your images. Your agent should research the API docs for whichever you pick:
- OpenAI —
gpt-image-1.5(ALWAYS 1.5, never 1). Needs an OpenAI API key. Best for realistic photo-style images. This is what Larry uses and what we strongly recommend. - Stability AI — Stable Diffusion XL and newer. Needs a Stability AI API key. Good for stylized/artistic images.
- Replicate — run any open-source model (Flux, SDXL, etc.). Needs a Replicate API token. Most flexible.
- Local — bring your own images. No API needed. Place images in the output directory and the script skips generation.
Conversion Tracking (optional but recommended for mobile apps)
- RevenueCat — this is what completes the intelligence loop. Upload-Post tells you which posts get impressions. RevenueCat tells you which posts drive paying users. Combined, the agent can distinguish between a viral post that makes no money and a modest post that actually converts — and optimize accordingly. Install the RevenueCat skill from ClaWHub (
clawhub install revenuecat) for full API access to subscribers, MRR, trials, churn, and revenue. There's also a RevenueCat MCP for programmatic control over products and offerings from your agent/IDE.
Cross-Posting (built-in with Upload-Post)
Upload-Post supports posting to TikTok, Instagram, YouTube, LinkedIn, Facebook, X (Twitter), Threads, Pinterest, Reddit, and Bluesky — all in a single API call. Your agent should research which platforms fit your audience and connect them in your Upload-Post profile. Same content, different algorithms, more reach.
First Run — Onboarding
The first run collects what the pipeline needs about the app: name, category, audience, value proposition, competitors to study, and the RevenueCat or analytics hookup used to attribute installs.
Run the onboarding script and answer its prompts:
node scripts/onboarding.js
It writes the answers to the skill's config and validates that GEMINI_API_KEY and
UPLOAD_POST_API_KEY resolve. If a required value is missing, ask the user rather than
inventing a default.
Full walkthrough, including every prompt and the category-specific guidance: references/onboarding.md.
Instructions
- Onboard once — capture the app, category, audience and competitors.
- Research competitors in the category to find the hooks already working.
- Generate slideshow images with
scripts/generate-slides.js. - Add hook text overlays with
scripts/add-text-overlay.js. - Post to TikTok and Instagram with
scripts/post-to-platforms.js. TikTok slideshows go up as drafts by design — see the note in the workflow below. - Track analytics with
scripts/check-analytics.jsand produce the daily report. - Feed the numbers back into the next batch's hook selection. Never skip this step.
Core Workflow
1. Generate Slideshow Images
Use scripts/generate-slides.js:
node scripts/generate-slides.js --config tiktok-marketing/config.json --output tiktok-marketing/posts/YYYY-MM-DD-HHmm/ --prompts prompts.json
The script auto-routes to the correct provider based on config.imageGen.provider. Supports OpenAI, Stability AI, Replicate, or local images.
⚠️ Timeout warning: Generating 6 images takes 3-9 minutes total (30-90 seconds each for gpt-image-1.5). Set your exec timeout to at least 600 seconds (10 minutes). If you get spawnSync ETIMEDOUT, the exec timeout is too short. The script supports resume — if it fails partway, re-run it and completed slides will be skipped.
Critical image rules (all providers):
- ALWAYS portrait aspect ratio (1024x1536 or 9:16 equivalent) — fills TikTok screen
- Include "iPhone photo" and "realistic lighting" in prompts (for AI providers)
- ALL 6 slides share the EXACT same base description (only style/feature changes)
- Lock key elements across all slides (architecture, face shape, camera angle)
- See references/slide-structure.md for the 6-slide formula
2. Add Text Overlays
This step uses node-canvas to render text directly onto your slide images. This is how Larry produces slides that have hit 1M+ views on TikTok — the text sizing, positioning, and styling are dialled in from hundreds of posts.
Setting Up node-canvas
Before you can add text overlays, your human needs to install node-canvas. Prompt them:
"To add text overlays to the slides, I need a library called node-canvas. It renders text directly onto images with full control over sizing, positioning, and styling — this is what Larry uses for his viral TikTok slides.
Can you run this in your terminal?"
npm install canvas"If that fails, it's because node-canvas needs some system libraries. Here's what to install first:"
macOS:
brew install pkg-config cairo pango libpng jpeg giflib librsvg npm install canvasUbuntu/Debian:
sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev npm install canvasWindows:
# node-canvas auto-downloads prebuilt binaries on Windows npm install canvas"Once installed, I can handle everything else — generating the overlays, sizing the text, positioning it perfectly. You won't need to touch this again."
Don't skip this step. Without node-canvas, the text overlays won't work. If installation fails, help them troubleshoot — it's usually a missing system library. Once it's installed once, it stays.
How Larry's Text Overlay Process Works
- Load the raw slide image into a node-canvas
- Configure text settings based on the text length for that specific slide
- Draw the text with white fill and thick black outline
- Review the output — check sizing, positioning, readability
- Adjust and re-render if anything looks off
- Save the final image once it looks right
Exact code Larry uses:
const { createCanvas, loadImage } = require('canvas');
const fs = require('fs');
async function addOverlay(imagePath, text, outputPath) {
const img = await loadImage(imagePath);
const canvas = createCanvas(img.width, img.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
// ─── Adjust font size based on text length ───
const wordCount = text.split(/\s+/).length;
let fontSizePercent;
if (wordCount <= 5) fontSizePercent = 0.075; // Short: 75px on 1024w
else if (wordCount <= 12) fontSizePercent = 0.065; // Medium: 66px
else fontSizePercent = 0.050; // Long: 51px
const fontSize = Math.round(img.width * fontSizePercent);
const outlineWidth = Math.round(fontSize * 0.15);
const maxWidth = img.width * 0.75;
const lineHeight = fontSize * 1.3;
ctx.font = `bold ${fontSize}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
// ─── Word wrap ───
const lines = [];
const manualLines = text.split('\n');
for (const ml of manualLines) {
const words = ml.trim().split(/\s+/);
let current = '';
for (const word of words) {
const test = current ? `${current} ${word}` : word;
if (ctx.measureText(test).width <= maxWidth) {
current = test;
} else {
if (current) lines.push(current);
current = word;
}
}
if (current) lines.push(current);
}
// ─── Position: centered at ~28% from top ───
const totalHeight = lines.length * lineHeight;
const startY = (img.height * 0.28) - (totalHeight / 2);
const x = img.width / 2;
// ─── Draw each line ───
for (let i = 0; i < lines.length; i++) {
const y = startY + (i * lineHeight);
// Black outline
ctx.strokeStyle = '#000000';
ctx.lineWidth = outlineWidth;
ctx.lineJoin = 'round';
ctx.miterLimit = 2;
ctx.strokeText(lines[i], x, y);
// White fill
ctx.fillStyle = '#FFFFFF';
ctx.fillText(lines[i], x, y);
}
fs.writeFileSync(outputPath, canvas.toBuffer('image/png'));
}
Key details that make Larry's slides look professional:
- Dynamic font sizing — short text gets bigger (75px), long text gets smaller (51px). Every slide is optimized.
- Word wrap — respects manual
\nbreaks but also auto-wraps lines that exceed 75% width. No squashing. - Centered at 28% from top — text block is vertically centered around this point, not pinned to it. Stays in the safe zone regardless of line count.
- Thick outline — 15% of font size. Makes text readable on ANY background.
- Manual line breaks preferred — use
\nin your text for control. Keep lines to 4-6 words.
Text content rules:
- REACTIONS not labels — "Wait... this is actually nice??" not "Modern minimalist"
- 4-6 words per line — short lines are scannable at a glance
- 3-4 lines per slide is ideal
- No emoji — canvas can't render them reliably
- Safe zones: No text in bottom 20% (TikTok controls) or top 10% (status bar)
The difference between OK slides and viral slides is in these details. Larry's slides consistently hit 50K-150K+ views because the text is sized right, positioned right, and readable at a glance while scrolling.
⚠️ LINE BREAKS ARE CRITICAL — Read This:
The texts.json file must contain text with \n line breaks to control where lines wrap. If you pass a single long string without line breaks, the script will auto-wrap, but manual breaks look much better because you control the rhythm.
Good (manual breaks, 4-6 words per line):
[
"I showed my landlord\nwhat AI thinks our\nkitchen should look like",
"She said you can't\nchange anything\nchallenge accepted",
"So I downloaded\nthis app and\ntook one photo",
"Wait... is this\nactually the same\nkitchen??",
"Okay I'm literally\nobsessed with\nthis one",
"Snugly showed me\nwhat's possible\nlink in bio"
]
Bad (no breaks — will auto-wrap but looks worse):
[
"I showed my landlord what AI thinks our kitchen should look like",
...
]
Rules for writing overlay text:
- 4-6 words per line MAX — short lines are scannable at a glance
- Use
\nto break lines — gives you control over the rhythm - 3-4 lines per slide is ideal — more lines are fine, they won't overflow
- Read it out loud — each line should feel like a natural pause
- No emoji — canvas can't render them, they'll show as blank
- REACTIONS not labels — "Wait... this is nice??" not "Modern minimalist"
The script auto-wraps any line that exceeds 75% width as a safety net, but always prefer manual \n breaks for the best visual result.
3. Post to TikTok + Instagram
Use scripts/post-to-platforms.js:
node scripts/post-to-platforms.js --config tiktok-marketing/config.json --dir tiktok-marketing/posts/YYYY-MM-DD-HHmm/ --caption "caption" --title "title"
This uploads all slide images and posts them to TikTok + Instagram (and any other configured platforms) simultaneously in a single API call via Upload-Post.
How it works:
- Reads slide images from the directory (slide1.png through slideN.png)
- Sends them to Upload-Post's
POST /upload_photosendpoint - Includes all configured platforms in one request
- Uses
async_upload=truefor background processing - Returns a
request_idfor tracking (saved inmeta.json)
No manual video-ID linking needed. Upload-Post tracks posts automatically by request_id. The upload history endpoint returns per-platform post URLs and success/failure status.
Caption rules: Long storytelling captions (3x more views). Structure: Hook → Problem → Discovery → What it does → Result → max 5 hashtags. Conversational tone.
Why We Post TikTok Slideshows as Drafts — Best Practice
For TikTok specifically, posts go as photo carousels. TikTok photo posts benefit enormously from trending sounds:
- Music is everything on TikTok. Trending sounds massively boost reach. The algorithm favours posts using popular audio.
- After posting, add music from TikTok's sound library — browse what's trending in your niche.
- Posts without music get buried. Silent slideshows look like ads and get skipped. A trending sound makes your content feel native.
This is the workflow that helped us hit 1M+ TikTok views and $670/month MRR. Don't skip the music step.
Instagram carousels don't need music — they work great as-is. Upload-Post handles both platforms with appropriate settings.
4. Track Analytics
Use scripts/check-analytics.js to pull platform analytics and upload history:
node scripts/check-analytics.js --config tiktok-marketing/config.json --days 3
The script:
- Fetches platform-level analytics (followers, impressions, reach, profile views)
- Fetches upload history for the last N days
- Groups uploads by
request_id(one post = multiple platform entries) - Shows per-post success/failure status and post URLs
- Saves a snapshot to
analytics-snapshot.json
No connection step needed. Unlike systems that require manually linking post IDs, Upload-Post tracks everything automatically by request_id. When you upload, the history immediately shows which platforms received the post and their post URLs.
The daily cron handles all of this automatically. It runs in the morning, checks the last 3 days, and generates a comprehensive report.
The Feedback Loop (CRITICAL — This is What Makes It Work)
This is the part that separates the pipeline from a slideshow generator. Every post is measured, and the next batch is chosen against what actually performed — not against a fixed template.
The rules that matter:
- Never post a batch without reading the previous batch's numbers first. Posting blind turns the loop into noise.
- Change one variable at a time — hook or CTA, not both — or you cannot attribute the result.
- Kill losers fast, scale winners slowly. A hook that underperforms twice is dead; one that wins once is not yet proven.
- Attribute to installs, not likes. Views without installs mean the hook worked and the offer did not.
Full mechanics, thresholds and the analytics queries: references/feedback-loop.md and references/analytics-loop.md.
Posting Schedule
Optimal times (adjust for audience timezone):
- 7:30 AM — catch early scrollers
- 4:30 PM — afternoon break
- 9:00 PM — evening wind-down
3x/day minimum. Consistency beats sporadic viral hits. 100 posts beats 1 viral.
Cross-Posting
Upload-Post supports posting the same content to 10+ platforms simultaneously in a single API call. Recommend:
- Instagram — especially strong for beauty/lifestyle/home (included by default)
- YouTube Shorts — long-tail discovery
- Threads — lightweight engagement driver
- LinkedIn — for B2B/professional apps
- Pinterest — strong for visual/home/design niches
Same slides, different algorithms, more surface area. Each platform's algo evaluates content independently. Upload-Post handles format requirements per platform automatically.
App Category Templates
See references/app-categories.md for category-specific slide prompts and hook formulas.
Output
Each batch produces the rendered slides, the posted URLs per platform, and a daily report tying hooks to results:
Batch 2026-08-29 — hook type: CONTRADICTION
tiktok ✅ draft created (publish from the app)
instagram ✅ https://instagram.com/p/C8xY2...
Yesterday's batch (CURIOSITY):
views 12,401 · saves 388 · installs 47 · CPI-equivalent €0.00
→ CONTRADICTION selected for today
Always report installs alongside views. Views without installs mean the hook worked and the offer did not, and that distinction drives the next decision.
Security
post-to-platforms.jsrequires--confirm-public(orLARRY_CONFIRM_PUBLIC=yes) before posting. It publishes to live feeds, so the approval is enforced in code rather than assumed from the workflow.- Requests are built with
fetchandFormData; no shell is involved anywhere in the pipeline, so captions and hooks cannot inject commands. - The API key lives in
config.json, which is gitignored. Scripts never print it.
Error Handling
- Gemini rate limit or quota — the free tier limits per minute as well as per day. Back off and retry; do not silently switch to a worse model mid-batch.
- Text overlay clipped or unreadable — regenerate the slide. A broken slide published is worse than a batch delayed.
- Upload-Post
401— the key was sent asBearer. UseAuthorization: Apikey <key>. - Instagram rejects the post — the account must be Business or Creator and linked to a Facebook Page; personal accounts cannot publish through the API.
- TikTok slideshow appears as a draft — that is intended, not a failure. See the best-practice note in the workflow: publishing from inside the app gets better reach.
- Analytics empty right after posting — platform metrics lag by hours. Do not treat an empty first read as a zero-performing post; wait for the next cycle.
Examples
Full daily batch
node scripts/competitor-research.js
node scripts/generate-slides.js
node scripts/add-text-overlay.js
node scripts/post-to-platforms.js
Read yesterday's numbers before choosing today's hook
node scripts/check-analytics.js
node scripts/daily-report.js
Common Mistakes
| Mistake | Fix |
|---|---|
| 1536x1024 (landscape) | Use 1024x1536 (portrait) |
| Font at 5% | Use 6.5% of width |
| Text at bottom | Position at 30% from top |
| Different rooms per slide | Lock architecture in EVERY prompt |
| Labels not reactions | "Wait this is nice??" not "Modern style" |
| Only tracking views | Track conversions — views without revenue = vanity |
| Same hooks forever | Iterate based on data, test new formats weekly |
| No cross-posting | Use Upload-Post to post everywhere simultaneously |
spawnSync ETIMEDOUT |
Exec timeout too short — image gen takes 3-9 min for 6 slides. Use a 10-minute timeout or generate slides one at a time |
Resources
- Onboarding walkthrough: references/onboarding.md
- Feedback loop mechanics: references/feedback-loop.md
- Analytics loop: references/analytics-loop.md
- Competitor research method: references/competitor-research.md
- Slide structure: references/slide-structure.md
- App category templates: references/app-categories.md
- RevenueCat integration: references/revenuecat-integration.md
- Upload-Post API documentation: https://docs.upload-post.com