PrismPoster Creator Tools

com.prismposterPublished by prismposter.comOfficial publisherv1.0.0Updated Oct 24, 2026

Social media frame sizes with sourced safe areas, plus aspect-ratio, bitrate and subtitle tools.

VerifiedStreamable HTTPWeb executableData & AnalyticsMedia & Design

Overview

AI-generated overview

Read-only calculators for video and social creators: sourced platform frame sizes and safe areas, aspect-ratio crops, bitrate, subtitles and tempo.

What you can do
Look up social media placements and compare frame sizes, aspect ratios, safe-area insets and usable boxes, with first-party source links and crop or letterbox plans for a source size. Check whether a caption, logo or button rectangle stays inside a placement's safe area and by how many pixels it intrudes. Simplify aspect ratios, align sizes to codec blocks, and compute centre-crop or letterbox plans for a target ratio. Estimate encoded file size, the bitrate that fits a size cap, and bits-per-pixel density. Convert subtitle text between SRT and WebVTT with a timing shift, cue count and parse warnings. Estimate BPM from tap timestamps, or get beat, bar and delay tables for a known BPM.
When to use it
Useful when preparing video or social posts and you want exact, sourced frame and safe-area figures instead of guessed pixel tables, or when you need quick aspect-ratio, bitrate, subtitle or tempo arithmetic during an edit.
Requirements
Either the hosted Streamable HTTP endpoint at prismposter.com, or the npm package @prismposter/creator-tools run locally over stdio (for example via npx), which needs Node.js. No account, API key, environment variable or header is declared.
Before you install
Tools are described as read-only, deterministic and free of network calls, so no data is written or sent. The hosted endpoint answers from a larger dataset than the local package and adds a capabilities tool, so results can differ between the two. Safe-area values are null where a platform publishes no fixed figure; that is intentional and should not be replaced with zeros.

Installation

In SourceWeft

  1. Open PrismPoster Creator Tools in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.

Use with an agent

Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.

Read https://sourceweft.com/mcp/com.prismposter%2Fcreator-tools/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.

Read the install guide the agent follows

Other MCP clients

Add this to your client's mcpServers config.

{
  "mcpServers": {
    "prismposter-creator-tools": {
      "type": "http",
      "url": "https://www.prismposter.com/api/mcp"
    }
  }
}

README

creator-tools

Zero-dependency TypeScript utilities for video and social creators: aspect-ratio maths, tap tempo, subtitle parsing and conversion, platform frame sizes with sourced safe areas, encoding calculators, colour-palette extraction, GIF encoding, image metadata, and C2PA / AI-provenance detection.

No runtime dependencies. No network calls. Works in the browser, in Node, and in a worker. Every number it returns is one you could check by hand — and where a platform doesn't publish a number, it says so instead of inventing one.

bash
npm install @prismposter/creator-tools

Why this exists

Most of these calculations are scattered across blog posts that quote each other, and a surprising number of the quoted figures are wrong. The one that started this library: every "TikTok safe area" pixel table on the open web is invented. TikTok publishes an overlay template, not a fixed inset — the occluded zone varies with frame dimensions, caption length, and which ad formats are running. There is no correct constant, so this library returns null for it and explains why, rather than shipping a plausible-looking number.

That's the rule the whole package follows:

  • Derived numbers are derived. A 9:16 frame at 1080 wide is 1920 tall because that's what the ratio says. Every size in social-sizes is asserted against its own declared ratio by the test suite.
  • Quoted numbers name their source. Every safe area links the first-party page it was read from. safeAreaSourceUrl(placement) returns that URL.
  • Unknown is a value, not a blank. A platform that publishes nothing gets an explicit kind: 'unpublished' with the reason attached.

Modules

aspect-ratio

Ratio arithmetic for frames about to be encoded. Handles non-integral cinema ratios (2.39:1) the same way it handles 16:9.

ts
import {  simplifyRatio, cropToRatio, padToRatio, alignToBlock,} from '@prismposter/creator-tools/aspect-ratio';
simplifyRatio({ width: 1920, height: 1080 });   // { w: 16, h: 9 }
cropToRatio({ width: 1920, height: 1080 }, { w: 9, h: 16 });// { result: { width: 608, height: 1080 },//   removed: { top: 0, right: 656, bottom: 0, left: 656 },//   areaKept: 0.3167 }
// Codecs want even dimensions; H.264 chroma subsampling makes an odd// width an encoder error or a silent rescale.alignToBlock({ width: 1081, height: 607 });      // { width: 1082, height: 608 }alignToBlock({ width: 1080, height: 1920 }, 16); // { width: 1088, height: 1920 }

An odd crop remainder always goes to the bottom/right edge. That's arbitrary, but it's consistent — splitting it differently per call is how a batch of clips ends up a pixel out of register with each other.

social-sizes

Platform frames and safe areas, with sources.

ts
import {  getPlacement, frameSize, safeAreaPx, safeAreaBox, safeAreaSourceUrl,} from '@prismposter/creator-tools/social-sizes';
const reels = getPlacement('instagram-reels')!;frameSize(reels);           // { width: 1080, height: 1920 }safeAreaPx(reels);          // { top: 269, bottom: 672, left: 65, right: 65 }safeAreaBox(reels);         // { width: 950, height: 979 }safeAreaSourceUrl(reels);   // Meta's Reels ads-guide page
const tiktok = getPlacement('tiktok-video')!;safeAreaPx(tiktok);         // null — and that is the correct answertiktok.safeArea.reason;     // explains why no fixed table can be right

Meta publishes Reels and Stories as 14% top, 35% bottom, 6% each side. On a 1080×1920 frame that's 269 / 672 / 65 px. The bottom third is not a typo.

safeAreaPx returning null is meaningful — surface it to your user as "this platform publishes no fixed safe area". Don't substitute zeros, and don't borrow another platform's numbers.

subtitles

SRT, WebVTT, and ASS/SSA parsing, conversion, and timing edits. Everything routes through one neutral Cue shape rather than converting format-to-format: six formats would otherwise need thirty converters, each a place a rounding rule can quietly differ.

ts
import { convert, parse, shift, scale, deoverlap }  from '@prismposter/creator-tools/subtitles';
const { output, warnings } = convert(srtText, 'vtt');
// Fix subtitles authored against a different frame rate.scale(parse(srtText).cues, 23.976 / 25);
// Nudge everything 500ms later; cues pushed before zero clamp to zero.shift(cues, 500);
// Clip overlaps so only one cue is ever on screen.deoverlap(cues);

Parsing is tolerant: a malformed block produces a warning and is skipped, rather than costing you the other 899 cues in the file. UTF-8 BOMs and CRLF endings are handled. VTT NOTE/STYLE blocks and inline tags (<v Roger>, <i>) are stripped; ASS override blocks ({\an8}) are too.

ASS stores centiseconds, so an ASS round-trip is lossy below 10 ms. convert() tells you so in warnings rather than letting you discover it later.

tempo

Tap tempo and the beat arithmetic an edit timeline needs.

ts
import { estimateTempo, beatMs, noteMs, beatGrid, snapToBeat }  from '@prismposter/creator-tools/tempo';
estimateTempo(tapTimestamps);// { bpm: 128.02, taps: 16, spreadMs: 8.4, discarded: 1 }
beatMs(120);                              // 500noteMs(120, 'quarter', { triplet: true }); // 333.333beatGrid(120, 10_000);                     // [0, 500, 1000, … 10000]snapToBeat(480, 120);                      // { timeMs: 500, deltaMs: 20 }

The estimator takes the median of a trailing window, not the mean. A tap sequence has two failure modes a mean handles badly: the first taps run late while the user finds the beat, and one missed tap creates an interval at roughly double the true period. Outliers are discarded against the median, because a mean would already have been dragged far enough to keep the bad interval inside tolerance.

spreadMs tells you how much to trust the result — under ~20 ms is a steady tapper, over ~60 ms means the estimate is soft. beatGrid computes each position from its index rather than accumulating, so a long sequence cannot drift.

encoding

File size, bitrate, timecode, and frame counts.

ts
import { fileSizeBytes, targetBitrateKbps, bitsPerPixel, toDropFrame }  from '@prismposter/creator-tools/encoding';
fileSizeBytes(60, 8_000);          // 60_000_000 bytestargetBitrateKbps(120, 50_000_000); // kbps to hit a 50 MB capbitsPerPixel(1920, 1080, 30, 8_000); // 0.1286toDropFrame(1_800, 29.97);           // '00:01:00;02'

Unit convention, stated once: bitrates are decimal (1 Mbps = 1,000,000 bits/s, as every encoder and platform spec means it) and file sizes are binary (1 MiB = 1,048,576 bytes, as every OS reports it). Mixing the two is why online calculators disagree by about 5%. formatBytesBinary and formatBytesDecimal are separate functions so the choice is always explicit.

Drop-frame timecode skips labels, never frames: numbers 00 and 01 are omitted at the start of each minute except every tenth. That's what keeps 29.97 timecode matching wall clock, which non-drop drifts from by ~3.6 s per hour.


palette

Median-cut colour quantisation, plus the contrast maths for putting text on what it returns.

ts
import { extractPalette, toHex, readableTextOn } from '@prismposter/creator-tools/palette';
const swatches = extractPalette(pixels, 5);  // pixels: { r, g, b }[]swatches.map(toHex);                          // ['#1b2430', '#3f6d8e', ...]readableTextOn(swatches[0]);                  // { onColour: 'black' | 'white', ratio: 8.7 }

The box split is chosen at the widest gap along the dominant axis, not at the median pixel count. The textbook median split gives every box the same number of pixels, which is exactly wrong for a photograph: a large flat sky gets subdivided repeatedly while a small saturated accent -- the one colour a designer actually wants -- is averaged into its neighbour and disappears. Splitting at the gap keeps it.

When the source has fewer distinct colours than requested slots you get fewer swatches. Padding the result with near-duplicates would be a palette that lies about the image.

provenance

Is there provenance on this file, and does it say the asset was AI-generated?

ts
import { inspectProvenance } from '@prismposter/creator-tools/provenance';
inspectProvenance(bytes);// { hasC2paManifest: true, hasIptcSourceType: true, hasProvenance: true,//   declaresAiGenerated: true, declaresCapture: false,//   digitalSourceTypes: ['.../digitalsourcetype/trainedAlgorithmicMedia'],//   assertionLabels: ['c2pa.actions.v2', 'c2pa.claim.v2'],//   manifestOffset: 41, signatureVerified: false }

Two markings are in circulation and they are constantly confused:

  1. A C2PA manifest -- a cryptographically signed Content Credential in a JUMBF box.
  2. An IPTC digital source type -- a plain XMP/EXIF string with no signature at all, which anyone can write with exiftool.

A file can carry either, both, or neither. They are reported separately because they carry entirely different weight, and a great many "AI detector" write-ups treat the second as though it were the first.

It does not verify the signature. signatureVerified is always false, deliberately. Proving a manifest is valid needs the full C2PA trust list and certificate-chain validation -- that is what c2patool and c2pa-node are for, and you should use them when trust is the question. This answers the cheaper question those are overkill for, and which is often impossible in a serverless runtime that cannot ship a native binary: is a credential present, and what does it claim? That is the check you want when triaging an upload queue and routing only the interesting files on to a real verifier.

A present manifest is not a trusted manifest. Anyone can sign anything with a self-signed certificate, including a lie.

image-metadata

Read what a file says about itself -- and, more usefully, notice when it says nothing.

ts
import { readImageMetadata, isStripped } from '@prismposter/creator-tools/image-metadata';
const meta = readImageMetadata(bytes);meta.format;   // 'jpeg' | 'png' | 'webp' | 'gif' | ...meta.fields;   // parsed EXIF / XMP / text fieldsisStripped(meta);

Most platforms strip metadata on upload, so an image with none is the normal case rather than a suspicious one. isStripped exists so you can tell a user that instead of showing them an empty table.

gif

An animated GIF encoder: global palette, LZW, sub-blocks, the lot.

ts
import { encodeGif, buildGlobalPalette, indexFrame } from '@prismposter/creator-tools/gif';
const palette = buildGlobalPalette(frames, 256);const bytes = encodeGif({ width, height, palette, frames });

GIF allows 256 colours and one global palette across every frame, so the quantiser matters more than the encoder does -- which is why it shares palette's median cut rather than carrying its own.

mcp (Model Context Protocol Server)

@prismposter/creator-tools ships a zero-dependency MCP stdio server so AI assistants (Claude Desktop, Cursor, Windsurf, Cline) can query verified platform safe areas, aspect-ratio crops, bitrates, and subtitle conversions without hallucinating pixel dimensions.

Add to claude_desktop_config.json or .cursor/mcp.json:

json
{  "mcpServers": {    "prismposter-creator-tools": {      "command": "npx",      "args": ["-y", "@prismposter/creator-tools"]    }  }}

Exposed MCP tools (all read-only, pure local calculations with an output schema):

  • get_social_media_placement — one placement's frame size, safe-area insets in pixels, usable box and first-party documentation URL (instagram-reels, youtube-shorts, tiktok-video, etc.), plus a crop / letterbox plan for your source size
  • list_social_media_placements — every placement, or one platform's, for browsing and comparison
  • check_safe_area — whether a caption, logo or button box stays inside a placement's safe area, and by how many pixels it intrudes past each edge; TikTok answers unknown with the reason
  • calculate_aspect_ratio — simplify any ratio, align to codec blocks, and compute centre-crop / letterbox plans for an arbitrary target ratio
  • estimate_video_bitrate — encoded file size (bytes, MiB, MB), the bitrate that fits a size cap, and bits-per-pixel density
  • convert_subtitles — convert between SRT, WebVTT and ASS/SSA with format detection and a timing shift
  • estimate_tempo — BPM from tap timestamps, or beat and bar lengths for a known BPM

A bad call returns an MCP tool error (isError: true) whose message says how to fix it, for example the valid placement ids for an unknown one.

A hosted version runs at https://www.prismposter.com/api/mcp (Streamable HTTP, no install). It answers from prismposter.com's larger placement dataset (which also accepts the camelCase ids used on the site), takes videoKbps and targetCapMb in its bitrate tool, converts SRT and WebVTT only, and adds a get_prismposter_capabilities tool. Both are listed in the official MCP Registry as com.prismposter/creator-tools.

Design rules

  1. Pure functions, no side effects, no I/O. Everything is synchronous and deterministic.
  2. Invalid input returns null, it does not throw. These functions sit behind text inputs where a half-typed value is normal, not exceptional.
  3. Rounding happens once, at the boundary. Sizes are whole pixels because that's what an encoder takes.
  4. null means "no answer exists", and is always distinguishable from zero.
  5. A wrong type still throws. Rule 2 is about values a user could type. It is not a licence to guess what a caller meant by handing a string to a function that reads bytes.

Development

bash
npm installnpm test          # 173 testsnpm run typechecknpm run build

Contributions welcome — see CONTRIBUTING.md. The bar for a new constant is a first-party source; the bar for a new function is a test that would fail without it.

The same maths, as browser tools

Every module here backs a free tool at prismposter.com/tools -- no account, no upload, no email. If you want the answer rather than the function, start there:

Who maintains this

PrismPoster is an AI content workspace -- image, video, remake, character and music studios that share one project, one asset library, and one timeline you can actually finish in.

This library is the boring half of that product: the arithmetic underneath the studios, extracted because it is genuinely reusable and because a subtitle parser is not a competitive advantage. provenance exists for the same reason in reverse -- building AI disclosure into a media product meant learning where the C2PA tooling is thin, and a dependency-free presence check was the piece that kept being missing.

License

MIT © PrismLabs OÜ

Source: README.md at commit da90a6e

Tools

7

get_social_media_placement

Get one social media placement spec

Look up one social media placement by id and return its frame size, aspect ratio, first-party safe-area insets in px, usable box, and the official page the figures came from. Pass sourceWidth and sourceHeight together to also get the centre-crop and letterbox plan that fits your footage to this placement. Use this when you know the target placement; use list_social_media_placements to browse or compare, check_safe_area to test whether an element is clear of the platform UI, and calculate_aspect_ratio for a ratio that is not a platform placement. For TikTok the insets are null with a reason, because TikTok publishes no fixed safe area. Errors on an unknown id (the message lists valid ids) or on only one source dimension.

ParametersidrequiredsourceWidthsourceHeight

list_social_media_placements

List social media placement specs

List every supported social media placement (or one platform's) with frame size, aspect ratio, safe-area insets in px, usable box and first-party source URL, plus links to the same dataset as CSV, JSON and its DOI. Use this to browse, compare platforms, or find a placement id; use get_social_media_placement when you already know the id and also need a crop/letterbox plan for your footage. The list is a fixed dataset returned whole, with no paging.

Parametersplatform

check_safe_area

Check an element against a placement safe area

Check whether a rectangle (a caption, logo or button, in px on the placement's own frame, origin top-left) stays inside the placement's safe area, and by how many px it intrudes past each edge if not. Use this to validate a layout before export; use get_social_media_placement for the raw insets. Returns verdict "inside", "intrudes" or "unknown"; "unknown" means the platform publishes no fixed safe area (TikTok) and comes with the reason instead of a guess. Errors on an unknown placement id or a box with non-positive size.

ParametersxrequiredyrequiredwidthrequiredheightrequiredplacementIdrequired

calculate_aspect_ratio

Calculate aspect ratio and crop/pad plan

Reduce any pixel size to its simplest aspect ratio (1920x1080 is 16:9) and the nearest even size an H.264 encoder accepts, and, given a target ratio, solve the missing width or height and return the centre-crop and letterbox/pillarbox plans with the px trimmed or added per edge. Use this for arbitrary sizes and ratios (cinema 2.39:1, a client's 4:5); when the target is a named platform placement, use get_social_media_placement with sourceWidth/sourceHeight instead, which picks the right ratio for you. Errors on a non-integer or out-of-range size, or on only one of targetRatioW/targetRatioH.

ParameterswidthrequiredheightrequiredtargetRatioHtargetRatioW

estimate_video_bitrate

Estimate video file size and bitrate

Video size and bitrate arithmetic for a clip of durationSec. Pass videoKbps to get the encoded file size (MB, GB, MiB, GiB); pass targetCapMb to get the highest video bitrate that fits that file size. Audio is added at audioKbps (default 192). Every answer also gives the highest video bitrate that fits Discord's 20 MB free upload limit. Kbps is decimal (1 kbps = 1000 bit/s) and MB is 1,000,000 bytes. Use it to plan an export or hit an upload cap; it does not inspect or encode files. Errors when neither videoKbps nor targetCapMb is given.

ParametersaudioKbpsvideoKbpsdurationSecrequiredtargetCapMb

convert_subtitles

Convert subtitles between SRT and WebVTT

Convert subtitle text between SubRip (srt) and WebVTT (vtt), optionally shifting every cue by shiftMs, and return the converted text with the detected source format, cue count, parse warnings (with line numbers) and timing issues (overlapping, zero-length or out-of-order cues). Pass the file contents, not a path. Malformed blocks are skipped with a warning instead of failing the whole file. ASS/SSA input is not supported here; the @prismposter/creator-tools package converts it. Errors when no cue could be parsed, the input is over 2 MB, or the shift is beyond one hour.

ParametersinputrequiredshiftMstoFormatrequired

estimate_tempo

Estimate tempo, delay times and bar lengths

Tempo arithmetic for editing to music: pass tap timestamps (ms) to estimate BPM with a stability and confidence rating, or pass a known bpm. Either way it returns the beat length, a delay table (straight, dotted and triplet ms for each note value) and how many seconds a number of bars lasts in a time signature. It works on numbers you supply and does not analyse audio. Errors when neither input is given, the taps do not yield a tempo, bpm is outside 30-300, or the time signature is not one listed.

ParametersbpmbarstimestampsMstimeSignature

Version history

2
  1. v1.0.0LatestOct 24, 2026
  2. v0.5.0Oct 23, 2026

Score

100% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 daysRequired25 pts
  • Auth declaredRequired10 pts
  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

Other Prismposter integrations