DEV Community

Cover image for A Tiny Node.js Client for Turning a Photo Into a Talking Video
TalkPix AI
TalkPix AI

Posted on

A Tiny Node.js Client for Turning a Photo Into a Talking Video

Every project that used our photo-to-talking-video API ended up with the same boilerplate: start a render, poll until it's done, upload local files through a signed URL, verify the webhook signature. So I packaged it.

npm install talkpix
Enter fullscreen mode Exit fullscreen mode

It has zero dependencies, runs on Node.js 18+ (built-in fetch) and ships its own TypeScript types.

Make a photo talk

import TalkPix from "talkpix";

const talkpix = new TalkPix({ apiKey: process.env.TALKPIX_API_KEY });

const video = await talkpix.videos.create({
  image_url: "https://example.com/portrait.jpg",
  script: "Hi! This video was made with one API call.",
  voice: "zephyr",
  language: "en-US",
});

const done = await talkpix.videos.waitForCompletion(video.id);
console.log(done.status, done.video_url);
Enter fullscreen mode Exit fullscreen mode

waitForCompletion polls until the render is completed or failed, so you get back a finished MP4 URL or a clear error.

Use your own audio instead of a script

Swap the script for a recording and the face lip-syncs to it:

const video = await talkpix.videos.create({
  image_url: "https://example.com/portrait.jpg",
  audio_url: "https://example.com/voiceover.wav",
});
Enter fullscreen mode Exit fullscreen mode

Upload local files

If the photo isn't at a public URL, upload it first and pass the returned key:

import { readFile } from "node:fs/promises";

const photo = await talkpix.uploadFile(await readFile("portrait.jpg"), {
  contentType: "image/jpeg",
});

await talkpix.videos.create({
  image_key: photo.key,
  script: "Uploaded straight from my server.",
  voice: "zephyr",
  language: "en-US",
});
Enter fullscreen mode Exit fullscreen mode

Check the cost before rendering

estimate is free and returns how many credits a script will reserve:

const { estimated_seconds, credits } = await talkpix.estimate({
  script: "Happy birthday, Sam!",
});
Enter fullscreen mode Exit fullscreen mode

Webhooks instead of polling

Pass a webhook_url and verify each delivery against the raw body:

import { verifyWebhookSignature } from "talkpix";

const ok = verifyWebhookSignature(
  process.env.TALKPIX_WEBHOOK_SECRET,
  req.get("TalkPix-Signature"),
  rawBody,
);
Enter fullscreen mode Exit fullscreen mode

It checks the HMAC, compares in constant time, and rejects stale timestamps.

Errors

Every API error is thrown as a TalkPixError with status, type and, for validation errors, the param that failed, so you can show users exactly which field to fix.

Links

API renders use pay-as-you-go credits (packs from $5, credits don't expire, no subscription required). If you just need one video and don't want to write code, the same thing runs as a talking photo app in the browser.

Disclosure: I work on TalkPix.

Top comments (0)