From 908e8a439521471f895affa9db03acf8033873b6 Mon Sep 17 00:00:00 2001 From: a-baran-orhan Date: Thu, 16 Jul 2026 16:56:17 +0300 Subject: [PATCH 1/2] Add product integration page --- playground/for-products.html | 162 +++++++++++ playground/index.html | 3 +- playground/public/sitemap.xml | 6 + playground/src/for-products.css | 387 +++++++++++++++++++++++++++ playground/src/for-products.ts | 21 ++ playground/test/for-products.test.ts | 38 +++ playground/vite.config.ts | 4 +- vercel.json | 6 +- vitest.config.ts | 2 +- 9 files changed, 624 insertions(+), 5 deletions(-) create mode 100644 playground/for-products.html create mode 100644 playground/src/for-products.css create mode 100644 playground/src/for-products.ts create mode 100644 playground/test/for-products.test.ts diff --git a/playground/for-products.html b/playground/for-products.html new file mode 100644 index 0000000..205cf03 --- /dev/null +++ b/playground/for-products.html @@ -0,0 +1,162 @@ + + + + + + Use Posecode in Your Product | Open-Source Motion Tools + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

For product owners & developers

+

Put movement inside your product.

+

Posecode turns a small, readable text document into an editable 3D movement. Use the open-source pieces now—or help shape the product infrastructure that should come next.

+ +
+ +
+ +
+
+

Ways to integrate

+

Start at the layer you need.

+

Keep the standard player, compose the lower-level libraries, or give a local agent movement tools.

+
+
+
+ 01 / embed +

Drop in a movement player

+

Use the framework-free <posecode-player> web component with inline Posecode, a .posecode URL, or a share token.

+
<script src="https://unpkg.com/posecode-embed@0.2.2/dist/posecode-embed.js"></script>
+<posecode-player src="/moves/squat.posecode"></posecode-player>
+
+
+ 02 / compose +

Own the interface

+

Parse text into a typed, range-of-motion-clamped IR, then drive the Three.js renderer inside your own editor, lesson, or workflow.

+
npm install posecode-parser posecode-render three
+
+
+ 03 / agents +

Run movement tools locally

+

The npm MCP server runs over stdio on your machine. It teaches an MCP client the language, validates documents, and creates playground links.

+
npx -y posecode-mcp@latest
+
+
+
+ +
+
+

Where it fits

+

Movement that stays inspectable.

+
+
+

Animation editors

Generate a first pass from text, expose timing and joint values, and let a human revise the movement instead of accepting a black-box clip.

+

Fitness & rehab products

Show an exercise beside a program or cue. Joint angles are clamped to general range-of-motion limits, but Posecode is not clinical advice or patient-specific validation.

+

Education tools

Connect written instruction to a moving figure for anatomy, dance, physical education, or movement vocabulary.

+

Movement libraries

Store small, diffable text files, validate them in CI, and open any movement in the browser without pre-rendering video.

+

Agent workflows

Let an MCP-capable local agent write, validate, repair, and return a link to movement the user can inspect.

+

Other web products

Use movement as editable content anywhere a video or static diagram is too rigid and a full animation pipeline is too heavy.

+
+
+ +
+
+

Available today

+

The current open-source surface.

+

These pieces are in the public repository and licensed Apache-2.0. You run them in your browser, build, or local environment.

+
+
+

posecode-embed

A web component with lazy loading, controls, reduced-motion behavior, events, and inline, URL, or share-token inputs.

browser
+

posecode-parser

A TypeScript parser and CLI that produce typed movement data, diagnostics, and range-of-motion clamps.

npm + CLI
+

posecode-render

A Three.js client-side renderer for a procedural figure or a compatible Mixamo-rigged GLB.

npm
+

posecode-share

A dependency-free codec for URL-safe documents and playground permalinks. The document lives in the URL; this is not private cloud storage.

npm
+

Share links

The public playground opens encoded movements for viewing and editing in the browser.

posecode.org
+

posecode-mcp

A local stdio MCP server for authoring guidance, validation, and render links. It does not render in the cloud.

local npm process
+
+

Operational boundary: there is currently no hosted product API, hosted MCP endpoint, account system, private content store, uptime commitment, or commercial support plan.

+
+ +
+
+

Exploring next

+

Useful infrastructure should be shaped by real products.

+

We are interested in working with a small number of design partners to learn which operational layer actually matters. Possibilities—not announced products—include:

+
+
    +
  • Hosted generation or validation API
  • +
  • Hosted MCP access
  • +
  • Cloud rendering and asset exports
  • +
  • Custom characters and rigs
  • +
  • Private movement libraries
  • +
  • White-label embeds
  • +
  • Usage analytics
  • +
  • Support, security review, and SLAs
  • +
+

None of these capabilities is generally available today. There is no published commercial pricing. A design-partner conversation is for scoping the problem, constraints, and a possible collaboration—not selling a service that already exists.

+
+ +
+

Bring a concrete problem

+

What movement does your product need to explain?

+

Tell us what you are building, where the animation would appear, and whether the open-source pieces cover the first version. We will reply from there.

+ Email hello@posecode.org → +
+
+ + + + + + diff --git a/playground/index.html b/playground/index.html index 81d6f00..6b8db42 100644 --- a/playground/index.html +++ b/playground/index.html @@ -137,7 +137,7 @@ Examples Movement library Developers - Feedback + For products GitHub Open playground → @@ -341,6 +341,7 @@

Common questions

Movement library  ·  Language spec  ·  LLM guide  ·  + For products  ·  Feedback  ·  Issues

diff --git a/playground/public/sitemap.xml b/playground/public/sitemap.xml index db7d86b..e1e201e 100644 --- a/playground/public/sitemap.xml +++ b/playground/public/sitemap.xml @@ -12,6 +12,12 @@ weekly 0.9 + + https://www.posecode.org/for-products + 2026-07-16 + monthly + 0.8 + https://www.posecode.org/moves/ 2026-07-16 diff --git a/playground/src/for-products.css b/playground/src/for-products.css new file mode 100644 index 0000000..2b3ff25 --- /dev/null +++ b/playground/src/for-products.css @@ -0,0 +1,387 @@ +.product-page main { + overflow: hidden; +} + +.product-page a:focus-visible { + outline: 3px solid var(--accent); + outline-offset: 4px; +} + +.product-hero, +.product-section, +.product-cta { + max-width: var(--container); + margin: 0 auto; + padding-left: 28px; + padding-right: 28px; +} + +.product-hero { + min-height: min(720px, calc(100vh - 69px)); + display: grid; + grid-template-columns: minmax(0, 1.08fr) minmax(360px, .72fr); + align-items: center; + gap: clamp(48px, 8vw, 112px); + padding-top: clamp(70px, 10vh, 116px); + padding-bottom: clamp(80px, 12vh, 140px); +} + +.product-hero h1 { + max-width: 11ch; + margin: 0 0 28px; + font-size: clamp(58px, 7.3vw, 102px); + font-weight: 900; + line-height: .88; + letter-spacing: -.065em; +} + +.product-lede { + max-width: 58ch; + margin: 0 0 32px; + color: var(--text-2); + font-size: clamp(18px, 1.7vw, 22px); + line-height: 1.5; +} + +.reality-card { + border-top: 2px solid var(--accent); + border-bottom: 1px solid var(--border); + padding: 28px 0 32px; +} + +.status-label { + display: flex; + align-items: center; + gap: 9px; + margin: 0 0 30px; + color: var(--accent); + font-family: var(--mono); + font-size: 11px; + font-weight: 600; + letter-spacing: .12em; + text-transform: uppercase; +} + +.status-label span { + width: 7px; + height: 7px; + background: var(--accent); + border-radius: 50%; +} + +.reality-card h2 { + margin: 0 0 18px; + font-size: clamp(25px, 3vw, 35px); + line-height: 1.08; + letter-spacing: -.035em; +} + +.reality-card > p:last-child, +.product-section-head > p:last-child, +.exploring-copy > p:last-child { + color: var(--text-2); +} + +.product-section { + padding-top: clamp(80px, 10vw, 140px); + padding-bottom: clamp(80px, 10vw, 140px); +} + +.product-section-head { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(280px, .7fr); + column-gap: 60px; + align-items: end; + margin-bottom: 52px; +} + +.product-section-head .eyebrow { + grid-column: 1 / -1; +} + +.product-section-head h2, +.exploring-copy h2, +.product-cta h2 { + margin: 0; + max-width: 13ch; + font-size: clamp(40px, 5.4vw, 68px); + line-height: .98; + letter-spacing: -.05em; +} + +.product-section-head > p:last-child { + margin: 0; + font-size: 17px; +} + +.integration-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + border-top: 1px solid var(--border); + border-left: 1px solid var(--border); +} + +.integration-card { + min-width: 0; + display: flex; + flex-direction: column; + min-height: 400px; + padding: 30px; + border-right: 1px solid var(--border); + border-bottom: 1px solid var(--border); +} + +.card-index, +.capability-row > span { + font-family: var(--mono); + color: var(--accent); + font-size: 11px; + letter-spacing: .11em; + text-transform: uppercase; +} + +.integration-card h3, +.use-case h3 { + margin: 48px 0 12px; + font-size: 23px; + line-height: 1.1; + letter-spacing: -.025em; +} + +.integration-card p, +.use-case p { + margin: 0; + color: var(--text-2); +} + +.integration-card pre { + max-width: 100%; + overflow-x: auto; + margin: auto 0 0; + padding: 17px; + border: 1px solid var(--border); + background: #080a0b; + color: var(--text); + font: 11px/1.7 var(--mono); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.integration-card pre code { + padding: 0; + border: 0; + background: none; + font: inherit; +} + +.use-cases { + border-top: 1px solid var(--border); +} + +.use-case-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + border-top: 1px solid var(--border); +} + +.use-case { + min-height: 230px; + padding: 28px 26px 32px 0; + border-bottom: 1px solid var(--border); +} + +.use-case:not(:nth-child(3n + 1)) { + padding-left: 26px; + border-left: 1px solid var(--border); +} + +.use-case h3 { + margin-top: 0; + font-size: 20px; +} + +.today-section { + border-top: 1px solid var(--border); +} + +.capability-list { + border-top: 1px solid var(--border); +} + +.capability-row { + display: grid; + grid-template-columns: minmax(190px, .75fr) minmax(0, 1.7fr) 150px; + gap: 30px; + align-items: baseline; + padding: 24px 0; + border-bottom: 1px solid var(--border); +} + +.capability-row h3, +.capability-row p { + margin: 0; +} + +.capability-row h3 code { + background: transparent; + border: 0; + padding: 0; + font-size: 14px; + color: var(--text); +} + +.capability-row p { + color: var(--text-2); +} + +.capability-row > span { + color: var(--muted); + text-align: right; +} + +.availability-note, +.future-qualifier { + margin: 30px 0 0; + padding: 20px 22px; + border-left: 2px solid var(--accent); + background: #0e1011; + color: var(--text-2); +} + +.availability-note strong { + color: var(--text); +} + +.exploring-section { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(340px, .8fr); + gap: clamp(50px, 8vw, 110px); + border-top: 1px solid var(--border); +} + +.exploring-copy h2 { + margin-bottom: 24px; +} + +.future-list { + list-style: none; + margin: 0; + padding: 0; + border-top: 1px solid var(--border); +} + +.future-list li { + position: relative; + padding: 14px 0 14px 25px; + border-bottom: 1px solid var(--border); + font-weight: 650; +} + +.future-list li::before { + content: "+"; + position: absolute; + left: 0; + color: var(--accent); + font-family: var(--mono); +} + +.future-qualifier { + grid-column: 1 / -1; + margin-top: 0; +} + +.product-cta { + margin-bottom: clamp(80px, 10vw, 140px); + padding-top: clamp(70px, 9vw, 110px); + padding-bottom: clamp(70px, 9vw, 110px); + text-align: center; + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + background: linear-gradient(180deg, #0e1011, transparent); +} + +.product-cta h2, +.product-cta > p { + max-width: 760px; + margin-left: auto; + margin-right: auto; +} + +.product-cta > p:not(.eyebrow) { + color: var(--text-2); + font-size: 18px; + margin-top: 22px; + margin-bottom: 30px; +} + +@media (max-width: 920px) { + .product-hero, + .exploring-section { + grid-template-columns: 1fr; + } + .product-hero { + min-height: auto; + } + .product-hero h1 { + max-width: 12ch; + } + .product-section-head { + grid-template-columns: 1fr; + gap: 22px; + } + .integration-grid, + .use-case-grid { + grid-template-columns: 1fr 1fr; + } + .use-case:not(:nth-child(3n + 1)) { + padding-left: 0; + border-left: 0; + } + .use-case:nth-child(even) { + padding-left: 26px; + border-left: 1px solid var(--border); + } +} + +@media (max-width: 620px) { + .product-hero, + .product-section, + .product-cta { + padding-left: 18px; + padding-right: 18px; + } + .product-hero { + gap: 64px; + padding-top: 64px; + padding-bottom: 84px; + } + .product-hero h1 { + font-size: clamp(51px, 16vw, 72px); + } + .integration-grid, + .use-case-grid { + grid-template-columns: 1fr; + border-left: 0; + } + .integration-card { + min-height: 340px; + padding: 26px 0; + border-right: 0; + } + .use-case, + .use-case:nth-child(even) { + min-height: 0; + padding: 25px 0; + border-left: 0; + } + .capability-row { + grid-template-columns: 1fr; + gap: 9px; + } + .capability-row > span { + text-align: left; + } + .exploring-section { + gap: 50px; + } +} diff --git a/playground/src/for-products.ts b/playground/src/for-products.ts new file mode 100644 index 0000000..fcba4ec --- /dev/null +++ b/playground/src/for-products.ts @@ -0,0 +1,21 @@ +import { inject } from "@vercel/analytics"; + +inject(); + +const prefersReducedMotion = matchMedia("(prefers-reduced-motion: reduce)").matches; + +if (!prefersReducedMotion && "IntersectionObserver" in window) { + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (!entry.isIntersecting) continue; + entry.target.classList.add("in"); + observer.unobserve(entry.target); + } + }, + { threshold: 0.08, rootMargin: "0px 0px -6% 0px" }, + ); + for (const element of document.querySelectorAll(".reveal")) observer.observe(element); +} else { + for (const element of document.querySelectorAll(".reveal")) element.classList.add("in"); +} diff --git a/playground/test/for-products.test.ts b/playground/test/for-products.test.ts new file mode 100644 index 0000000..5205c2e --- /dev/null +++ b/playground/test/for-products.test.ts @@ -0,0 +1,38 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; + +const root = resolve(import.meta.dirname, "../.."); +const page = readFileSync(resolve(root, "playground/for-products.html"), "utf8"); +const home = readFileSync(resolve(root, "playground/index.html"), "utf8"); +const vercel = JSON.parse(readFileSync(resolve(root, "vercel.json"), "utf8")) as { + redirects: Array<{ source: string; destination: string }>; + rewrites: Array<{ source: string; destination: string }>; +}; + +describe("product integration page", () => { + it("publishes the clean route and makes it discoverable", () => { + expect(vercel.rewrites).toContainEqual({ source: "/for-products", destination: "/for-products.html" }); + expect(vercel.redirects).toContainEqual({ source: "/for-products.html", destination: "/for-products", permanent: true }); + expect(home).toContain('href="/for-products"'); + }); + + it("includes canonical and social metadata", () => { + expect(page).toContain("Use Posecode in Your Product"); + expect(page).toContain('rel="canonical" href="https://www.posecode.org/for-products"'); + expect(page).toContain('property="og:title"'); + expect(page).toContain('name="twitter:card"'); + }); + + it("keeps current and potential capabilities explicitly separate", () => { + expect(page).toContain('id="today"'); + expect(page).toContain('id="exploring"'); + expect(page).toContain("No hosted commercial platform yet"); + expect(page).toContain("None of these capabilities is generally available today"); + expect(page).toContain("There is no published commercial pricing"); + }); + + it("provides an integration-specific contact action", () => { + expect(page).toContain("mailto:hello@posecode.org?subject=Posecode%20product%20integration"); + }); +}); diff --git a/playground/vite.config.ts b/playground/vite.config.ts index 076bb7c..5064dfd 100644 --- a/playground/vite.config.ts +++ b/playground/vite.config.ts @@ -18,9 +18,11 @@ export default defineConfig({ build: { rollupOptions: { input: { - // Build files behind the public `/` and `/play[/movement]` routes. + // Build files behind the public `/`, `/play[/movement]`, and + // `/for-products` routes. main: resolve(here, "index.html"), play: resolve(here, "play.html"), + forProducts: resolve(here, "for-products.html"), }, }, }, diff --git a/vercel.json b/vercel.json index 62b7d5d..ba111b2 100644 --- a/vercel.json +++ b/vercel.json @@ -6,10 +6,12 @@ "outputDirectory": "playground/dist", "redirects": [ { "source": "/index.html", "destination": "/", "permanent": true }, - { "source": "/play.html", "destination": "/play", "permanent": true } + { "source": "/play.html", "destination": "/play", "permanent": true }, + { "source": "/for-products.html", "destination": "/for-products", "permanent": true } ], "rewrites": [ { "source": "/play", "destination": "/play.html" }, - { "source": "/play/:movement", "destination": "/play.html" } + { "source": "/play/:movement", "destination": "/play.html" }, + { "source": "/for-products", "destination": "/for-products.html" } ] } diff --git a/vitest.config.ts b/vitest.config.ts index ad9d188..af05dbd 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { - include: ["packages/**/test/**/*.test.ts"], + include: ["packages/**/test/**/*.test.ts", "playground/test/**/*.test.ts"], coverage: { provider: "v8", include: ["packages/posecode-parser/src/**/*.ts"], From 96300c557a3c9472c3982c9a5faf2a2e1de3469c Mon Sep 17 00:00:00 2001 From: a-baran-orhan <a.baranorhan@gmail.com> Date: Thu, 16 Jul 2026 17:10:04 +0300 Subject: [PATCH 2/2] Add empty changeset for website update --- .changeset/wise-deer-do.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/wise-deer-do.md diff --git a/.changeset/wise-deer-do.md b/.changeset/wise-deer-do.md new file mode 100644 index 0000000..a845151 --- /dev/null +++ b/.changeset/wise-deer-do.md @@ -0,0 +1,2 @@ +--- +---