A deterministic meme editor that turns an uploaded image, GIF, or short video clip into a mobile-share GIF.
This is not an AI generator. It is a small editor: pick media, add text, position it, trim video if needed, and export a GIF with FFmpeg.
- Search for GIFs with GIPHY or upload your own media.
- Add a quick caption or remix top/bottom meme text.
- Drag text directly on the preview before rendering.
- Trim short video sources.
- Export a shareable GIF with no audio.
- Client: React 19 + Vite
- Server: Express + Multer
- Rendering: FFmpeg + FFprobe
- Tests: Node test runner
- Deployment: Docker web service on Render
Prerequisites:
- Node.js 20+
- npm
ffmpegandffprobeon PATH- a bold font available to FFmpeg, such as DejaVu Sans Bold
Install everything:
npm run install-allStart the app:
npm run devLocal URLs:
- Client:
http://127.0.0.1:5173 - API:
http://127.0.0.1:5000
The app is set up to deploy as one Docker web service on Render.
Included deployment files:
Dockerfilerender.yaml
Render runs the Express server, serves the built React app, installs FFmpeg in the container, and uses /api/health as the health check.
Free-tier notes:
- Free services can spin down after idle time.
- Generated GIFs are temporary because the filesystem is ephemeral.
- For durable media storage, add object storage or a paid persistent disk.
Useful variables:
PORTMAX_UPLOAD_SIZEMAX_REMOTE_HTML_SIZEALLOW_PRIVATE_MEDIA_URLSGIPHY_API_KEYFFMPEG_BINFFPROBE_BINFONT_PATHVITE_API_BASE_URLVITE_API_PROXY_TARGET
Set GIPHY_API_KEY if you want featured GIFs and GIF search.
Remote media imports reject private, loopback, link-local, and other non-public network addresses by default. Keep ALLOW_PRIVATE_MEDIA_URLS unset or false in production.
Main endpoints:
GET /api/healthGET /api/templatesGET /api/gifs/featuredGET /api/gifs/search?q=<query>POST /api/renders
POST /api/renders accepts multipart form-data:
media- uploaded image/video/GIF filemediaUrl- direct media URL, or a supported YouTube page URL when a direct stream is exposedpresetIdcaptiontopTextbottomTexttextLayout- optional JSON map of text slot anchorsstartSeconds- video onlydurationSeconds
The response includes the rendered GIF URL, output metadata, and render metadata.
npm test
npm run lint
npm run buildclient/ React editor
server/
src/
app.js app factory
routes/api.js HTTP routes
presets/ editor profiles
services/ FFmpeg, GIPHY, remote media, render pipeline
validation/ request normalization
uploads.js multipart handling
- AI image or video generation
- full YouTube signature-decipher support
- large template marketplace
- sticker layers or multi-scene timelines
- user accounts or durable media storage
