npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bantam-hq/featherweight

v0.1.3

Published

Turn a screenplay PDF into Fountain, FDX, or structured JSON.

Downloads

784

Readme

Featherweight

Turn a screenplay PDF into Fountain, FDX, or structured JSON.

Featherweight reads the text, styling, and layout already in a PDF and puts the screenplay back together. It knows the difference between a scene heading and action, a character cue and dialogue, or a centered title and a transition. The result is an editable screenplay—not a pile of lines copied out of a PDF.

Features

  • Fast — converts a 120-page feature screenplay into an editable file in an average of 177 ms, with no model or external service.
  • Screenplay-aware — recognizes title pages, scene headings, action, characters, parentheticals, dialogue, dual dialogue, lyrics, transitions, centered text, and page breaks.
  • Keeps the writing intact — preserves inline emphasis, scene numbers, character extensions, deliberate spacing, and source ordering.
  • Three useful outputs — clean Fountain, ready-to-open FDX, or canonical JSON for your own application.
  • Flexible page routing — choose native extraction or reserve a page for OCR through the same conversion API.
  • Works where you do — ESM and TypeScript types for Node.js, plus a small command-line tool.

Install

Featherweight requires Node.js 24 or newer.

npm install @bantam-hq/featherweight

For the command-line tool:

npm install --global @bantam-hq/featherweight

Command line

Point Featherweight at a PDF. Fountain goes to stdout by default.

featherweight screenplay.pdf

Choose another format or write straight to a file:

featherweight screenplay.pdf --output screenplay.fountain
featherweight screenplay.pdf --format json --output screenplay.json
featherweight screenplay.pdf --format fdx --output screenplay.fdx
Usage: featherweight <input.pdf> [--format fountain|json|fdx] [--output <path>]

The format comes from --format, not the output filename. An existing output file is replaced, and its parent directory must already exist. Run featherweight --help for help or featherweight --version for the installed version.

Node.js API

The API has two jobs: inspect the PDF, then turn it into the format you want.

Inspect a PDF

import { readFile } from "node:fs/promises";
import { inspectScreenplayPdf } from "@bantam-hq/featherweight";

const pdfBytes = new Uint8Array(await readFile("screenplay.pdf"));
const inspection = inspectScreenplayPdf(pdfBytes);

console.log(inspection.pageCount);
console.log(inspection.pagesNeedingOcr);
function inspectScreenplayPdf(pdfBytes: Uint8Array): PdfInspection;

interface PdfInspection {
  readonly pageCount: number;
  readonly pagesNeedingOcr: readonly number[];
}

Page indexes are zero-based.

Convert a screenplay

Pass the original PDF and choose which pages should use native text extraction and which should be reserved for OCR. Featherweight handles extraction, screenplay recognition, and serialization behind the public API.

import { readFile } from "node:fs/promises";
import {
  inspectScreenplayPdf,
  screenplayToFDX,
  screenplayToFountain,
  screenplayToJSON,
} from "@bantam-hq/featherweight";

const pdfBytes = new Uint8Array(await readFile("screenplay.pdf"));
const inspection = inspectScreenplayPdf(pdfBytes);
const nativePageIndexes = Array.from(
  { length: inspection.pageCount },
  (_, pageIndex) => pageIndex,
);

const fountain = screenplayToFountain(pdfBytes, nativePageIndexes, []);
const json = screenplayToJSON(pdfBytes, nativePageIndexes, []);
const fdx = screenplayToFDX(pdfBytes, nativePageIndexes, []);
function screenplayToJSON(
  pdfBytes: Uint8Array,
  nativePageIndexes: readonly number[],
  ocrPageIndexes: readonly number[],
): string;

function screenplayToFountain(
  pdfBytes: Uint8Array,
  nativePageIndexes: readonly number[],
  ocrPageIndexes: readonly number[],
): string;

function screenplayToFDX(
  pdfBytes: Uint8Array,
  nativePageIndexes: readonly number[],
  ocrPageIndexes: readonly number[],
): string;

Page indexes are zero-based. Each index belongs in one routing array at most, and the arrays can arrive in any order. Pages routed to native extraction are read directly from the PDF. OCR-routed pages currently preserve their physical place without contributing text; an OCR service adapter will fill that route in a later release. Featherweight never mutates the PDF bytes or routing arrays.

Only physical page 0 is treated as a possible title page. Every title field is optional.

Outputs

Fountain

Plain-text Fountain that keeps the screenplay editable and portable. Featherweight adds syntax only where Fountain needs it to preserve the recognized screenplay. Screenplay text that resembles Fountain syntax is emitted verbatim because Fountain defines escaping only for emphasis, so Fountain applications may interpret that text as syntax.

FDX

A valid Final Draft document with the screenplay's complete text, title information, element structure, styles, scene numbers, dual dialogue, and page breaks.

JSON

The canonical screenplay document for applications that want the structure directly. It includes title fields, semantic elements, styled text runs, scene numbers, alignment, dual-dialogue ownership, and page breaks.

Errors

PDF inspection throws PdfInspectionError. Screenplay conversion throws ScreenplayConversionError. Both expose a stable .code:

type PdfInspectionErrorCode =
  | "PDF_BINDING_UNAVAILABLE"
  | "PDF_INSPECTION_FAILED";

type ScreenplayConversionErrorCode =
  | "INVALID_PAGE_INDEX"
  | "DUPLICATE_PAGE_INDEX"
  | "OVERLAPPING_PAGE_INDEX"
  | "PDF_PROCESSING_FAILED"
  | "INVALID_FDX_TEXT";

Development

pnpm install --frozen-lockfile
pnpm check

pnpm check runs type checking, the full unit and integration suite, a production build, and packaged CLI tests. CI runs it on Linux, macOS, and Windows.

Acknowledgements

Featherweight builds on and learned from some excellent open-source work:

  • PDF Inspector by Firecrawl powers fast native PDF classification and text extraction.
  • Fountain, created by John August, Stu Maschwitz, and Nima Yousefi, defines the plain-text screenplay format. Stu and John also created a number of fountain docs used for testing.
  • Screenplain by Martin Vilcans was a useful reference for simple FDX output.

Licenses and fixture attribution are in THIRD_PARTY_NOTICES.md.

License

MIT © 2026 Bantam HQ