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

rmscene-ts

v0.1.1

Published

Read, write, and render reMarkable .rm version 6 files in TypeScript

Readme

rmscene-ts

rmscene-ts reads, writes, and renders reMarkable .rm version 6 scene files in TypeScript.

It is a full rewrite in TypeScript of rmscene 0.8.0 by Rick Lupton, pinned to commit cf86cf0374ca43a53477dd27c65fe2e70e6b4750. That Python project worked out the format and remains the behavioral reference: every reader golden in this repository is generated by running it. What changed in the rewrite is listed under What this port changes.

The package has no runtime dependencies, accepts and returns Uint8Array, works in Node 20+ and ES2022 browsers, and ships ESM, CommonJS, and TypeScript declarations. It performs no filesystem, network, credential, tablet, or cloud operations.

Install

npm install rmscene-ts

Read a scene

import { readBlocks, readText, readTree, type ReadWarning } from "rmscene-ts";

const data = new Uint8Array(await file.arrayBuffer());
const warnings: ReadWarning[] = [];
const options = { onWarning: (warning: ReadWarning) => warnings.push(warning) };

const blocks = readBlocks(data, options);
const tree = readTree(data, options);
const document = readText(tree, options);

console.log(tree.sceneInfo?.paperSize, document);
for (const item of tree.walk()) {
  if (item.kind === "line") console.log(item.tool, item.color, item.colorRgba, item.points);
}

Warnings are delivered only through onWarning; the library does not write to the console. Set strict: true to throw on malformed known blocks. Forward-compatible trailing bytes and unknown numeric values remain retained in both modes.

Write .rm bytes

import { readBlocks, writeBlocks } from "rmscene-ts";

const blocks = readBlocks(data);
const unchanged = writeBlocks(blocks);

The default writer preserves parsed block versions, optional-field presence, line timestamps, unknown blocks, incomplete final block headers, extraData, and nested extraValueData. For every included fixture, writeBlocks(readBlocks(data)) returns the exact original bytes.

Pass an explicit target software version when generating new content or intentionally normalizing field layout according to the Python writer:

import { simpleTextDocument, writeBlocks } from "rmscene-ts";

const blocks = simpleTextDocument("Hello", { version: "3.27.3.0" });
const data = writeBlocks(blocks, { version: "3.27.3.0" });

The writer serializes blocks. It does not flatten a modified SceneTree back into blocks or update a tablet's document manifests. Keep the original block array when making lossless edits.

Render SVG previews

import { readTree, renderSvg } from "rmscene-ts";

const tree = readTree(data);
const preview = renderSvg(tree, {
  viewport: "content",
  background: "white",
});

preview.svg;
preview.viewBox;
preview.strokeCount;
preview.text;

The renderer reads page dimensions from SceneInfo, keeps the horizontally centered stroke coordinate system, expands a content viewport beyond page bounds, uses Paper Pro RGBA colors when present, escapes typed text, and preserves leading and trailing typed whitespace. A scene without SceneInfo.paperSize requires an explicit paperSize: [width, height]; no device size is guessed.

This is a deterministic preview renderer. It preserves geometry, ordering, colors, text, visibility, and bounds, but does not emulate every proprietary brush texture, pressure shader, or font metric in xochitl.

Reader behavior

| Condition | Default mode | Strict mode | |---|---|---| | Wrong version 6 header | throws RmParseError | throws RmParseError | | Malformed known block | returns UnreadableBlock, warns, continues at its declared end | throws RmParseError | | Truncated final block header or payload | retains the incomplete bytes in an UnreadableBlock, warns | throws RmParseError | | Unknown block type | retains raw payload in UnreadableBlock, warns | same | | Unknown pen, color, paragraph style, or text code | retains numeric value, warns | same | | Unread trailing data | retains bytes in extraData or extraValueData, warns | same |

Pens, colors, and paragraph styles are open numeric values. A known tool is represented as { value: 23, name: "SHADER" }; a future tool remains observable as { value: 255 }. CrdtId.part2 is a bigint.

Paper Pro compatibility

| Input or environment | Support | |---|---| | .rm version 6 | Yes | | reMarkable Paper Pro, software 3.27.3.0 | Tested with sanitized fixtures | | Files in the rmscene 0.8.0 test suite | All 13 fixtures match Python goldens | | Browser | ES2022 with TextEncoder, TextDecoder, DataView, and BigInt | | Node | 20 and newer | | Older .rm versions | No |

Tested Paper Pro scenes contain both 1620 x 2160 and 1404 x 1872 pages. Coordinates can be negative or exceed the page after the infinite canvas is scrolled. Highlighters can carry both a palette index and an actual RGBA color.

Forward-compatible trailing data is expected. Sanitized fixtures retain 111 unread bytes in Paper Pro SceneInfo, 18 in RootText, and five nested bytes on each of nine SHADER lines.

What this port changes

The port is not a transliteration. These are the differences against rmscene 0.8.0, each covered by tests in this repository.

Fixed for reMarkable Paper Pro

  • Firmware trailing bytes are data, not corruption. Paper Pro writes fields the 0.8.0 model does not know: about 111 unread bytes in every SceneInfo block and about 18 in every RootText. The reader keeps them in extraData and extraValueData, so a read and write round trip returns them untouched instead of dropping them.
  • Unknown pens, colors, and text codes stay observable. Firmware-controlled sets are open values, { value, name? }, never closed enums, so a pen or color that a later firmware invents survives a read and a write instead of failing validation. The text formatting codes 1 and 2, which 0.8.0 reports as unknown, keep their raw number for the caller to act on.
  • Page size comes from the file. SceneInfo.paperSize decides the page, so 1620 x 2160 Paper Pro pages are not clipped to the reMarkable 2 size.
  • Highlighter RGBA is exposed next to the palette index, because Paper Pro carries both.
  • Truncated files keep their last bytes. A file that ends part way into the next block header keeps those exact bytes and writes them back unchanged, instead of being repaired or discarded.

A Rust parser, remarkable_lines, was evaluated as an alternative before this port was written, and rejected: it failed on five of eight real Paper Pro pages, with Block type '7' did not read expected size on the trailing bytes above, and Invalid tool with value '23' on the shader pen that firmware 3.x ships and rmscene 0.8.0 already knows. Both failure modes are regression-tested here.

Added on top of the Python library

  • A byte-exact writer. With no target version, every block writes back the header versions, field presence, timestamps, and trailing data it was read with, so writeBlocks(readBlocks(data)) returns the original bytes for all 17 fixtures. The Python writer normalizes to a target software version; that mode is still available here through WriteOptions.version and is verified against Python output hashes.
  • An SVG preview renderer. rmscene has none; rendering lives in the separate rmc tool. This package renders scenes deterministically in the browser too, including page bounds, off-page geometry, visibility, palette and RGBA colors, glyph ranges, and typed text with preserved whitespace.
  • Warnings as an API. Tolerant reading is inherited from the Python library, but recovery is reported through an onWarning callback and a strict switch instead of logging, so a caller can decide per file what to do.
  • Hardened bounds. Lengths and counts are checked before slicing or allocating, varuints are capped at the ten bytes a uint64 needs, and every block and nested field parses inside its own cursor, so a malformed child cannot eat the field that follows it.
  • Paper Pro test coverage. Four sanitized Paper Pro fixtures, with every text character replaced by x, sit next to the 13 upstream fixtures, and both sets are checked against Python goldens.

Platform

  • No runtime dependencies, ESM and CommonJS builds with TypeScript declarations.
  • Uint8Array in and out, no filesystem, network, or device access, so it runs in a browser as well as in Node 20 and newer.
  • CrdtId.part2 is a bigint, which the format needs and JavaScript numbers cannot hold.

Verification and development

The test suite contains all 13 upstream fixtures and four sanitized Paper Pro fixtures. It compares reader output and explicit writer versions with goldens produced by the pinned Python implementation, requires byte-exact lossless round trips, and snapshots deterministic SVG output. It also covers corruption, truncation, random input, unknown values, field presence, ESM, CommonJS, declarations, and the packed package surface.

npm ci
npm run check
npm audit
npm pack --dry-run

Use npm run goldens for reader goldens and npm run goldens:writer for writer hashes with the exact pinned ../rmscene checkout. Fixture provenance is in tests/fixtures/README.md. The codec design is in docs/architecture/rm-v6-codec.md.

The three packages

| Package | What it does | | --- | --- | | rmscene-ts (npm) | Reads, writes and renders .rm version 6 scene files. No filesystem, no network, browser-safe. | | rmcommunication-ts (npm) | Talks to the tablet over pinned SSH: listings, verified rmdoc backups, page rendering, templates, PNG, PDF and EPUB import. | | remarkable-cli (npm) | The rmcli command line over both libraries. |

None of them implements the reMarkable Cloud protocol.

Direct SSH/SFTP, document bundles, backups, templates, PNG output, mirror, and guarded page writeback belong to rmcommunication-ts, not here.

License and lineage

MIT. This port is derived from rmscene by Rick Lupton; its original copyright and MIT license are preserved in LICENSE. The upstream test fixtures are redistributed under the same license.