# Adapter Implementation Source: https://docs.elata.bio/adapter-implementation Build a device adapter that plugs into BleTransport and emits stable headband frames. Once your protocol inventory is complete, build the adapter. The goal is to isolate device-specific BLE and packet logic while preserving the shared Elata transport contract for apps. ## Recommended Shape For browser BLE integrations, the adapter should satisfy the `BleDeviceLike` shape expected by `@elata-biosciences/eeg-web-ble`. That means the device layer handles discovery, session setup, subscription, and packet decoding, while `BleTransport` handles the app-facing transport surface. ## Implementation Order Define the `requestDevice` filters and any optional services required to find the device cleanly in the browser picker. Connect to GATT, resolve characteristics, and do any startup writes that are required before streaming can begin. Turn vendor packets into `number[][]` where each row is one time step and the row width matches `numEegChannels`. Expose `samplingRate`, `eegNames`, `numEegChannels`, and related device metadata accurately. Unsubscribe, stop the stream safely, and release the session without leaking resources. ## Minimal Adapter Skeleton ```ts theme={null} import type { BleDeviceLike } from "@elata-biosciences/eeg-web-ble"; export class VendorBleDevice implements BleDeviceLike { isAthena = false; samplingRate = 250; eegNames = ["CH1", "CH2"]; numEegChannels = 2; opticsChannelCount = 0; getBoardInfo() { return { device_name: "VENDOR_DEVICE" }; } getCharacteristicInfo() { return { characteristics: [] }; } async prepareSession() { // Connect to GATT, resolve characteristics, and initialize streaming state } async releaseSession() { // Tear down subscriptions and release the browser session } async startStream(eegCb, _ppgCb) { // Subscribe to notifications and emit decoded EEG rows eegCb([[0, 0]]); } async stopStream() { // Stop device streaming and unsubscribe } } ``` ## What the Adapter Must Own | Responsibility | Adapter owns it? | | - | - | | Device discovery | yes | | GATT connection and characteristic setup | yes | | Packet decoding | yes | | Accurate metadata | yes | | App-facing transport lifecycle | usually through `BleTransport` | | Normalized frame delivery to apps | through the shared transport contract | ## Packet Decoding Rules Keep the decoder boring and deterministic. | Rule | Why it matters | | - | - | | Reject malformed packets early | Prevents bad rows from reaching apps | | Preserve channel order | Keeps downstream features and models consistent | | Batch rows only when needed | Avoids hidden latency and timestamp confusion | | Track packet counters when available | Helps detect drops or corruption | | Separate EEG from aux logic cleanly | Makes debugging and testing easier | ## Session Lifecycle Rules Your adapter should make these transitions unsurprising: | Stage | Good behavior | | - | - | | Discover | only matching devices are shown | | Connect | required services and characteristics resolve clearly | | Start | stream begins once, without duplicate subscriptions | | Stop | notifications stop cleanly | | Release | the session can be started again later | ## Reliability Notes `startStreaming()` is the safest default for app consumers because it combines connect and start. Your adapter should still make the underlying `connect()` and `start()` steps reliable on their own. If the device exposes auxiliary signals such as PPG, optics, IMU, or battery, map them only when the semantics are clear. Do not force Muse-specific assumptions onto non-Muse hardware. ## Related Docs * [EEG BLE Getting Started](/sdk/eeg-web-ble/getting-started) * [Transport Contract](./transport-contract) * [Testing and Validation](./testing-and-validation) * [Muse Device](/sdk/eeg-web-ble/muse-device) ## Next Turn the adapter into a reliable transport under real failure conditions. Package the integration with the docs and caveats other teams need. # Claude Code Source: https://docs.elata.bio/apps/ai-tools/claude-code Use Claude Code to build Elata apps with the Elata SDK and Elata APIs. ## Overview Claude Code works best on Elata apps when it knows how Elata works: the sandbox, the SDK packages, and the upload rules. This page gives you a project context file to paste in, plus prompts for common tasks. *** ## Setup 1. Install Claude Code by following the [Claude Code docs](https://docs.claude.com/en/docs/claude-code/overview). 2. Open a terminal in your project and run `claude`. 3. Add a `CLAUDE.md` file at your project root with the context below. *** ## Project context Save this as `CLAUDE.md` at your project root: ```markdown CLAUDE.md theme={null} # Elata app This is a browser app for Elata (https://app.elata.bio), built on the Elata SDK. It is a static web build that runs inside a sandboxed iframe in Elata. ## Docs - Docs home: https://docs.elata.bio (full text for agents: https://docs.elata.bio/llms-full.txt) - Building apps: https://docs.elata.bio/apps/build/overview - Host bridge (Elata APIs): https://docs.elata.bio/apps/platform/host-bridge - SDK overview: https://docs.elata.bio/sdk/overview - Package references for agents: node_modules/@elata-biosciences//llms.txt ## Packages - @elata-biosciences/rppg-web: camera heart rate (use createRppgSession) - @elata-biosciences/eeg-web + eeg-web-ble: EEG over Web Bluetooth (Muse built in) - @elata-biosciences/ppg-web: headband PPG heart rate and HRV - @elata-biosciences/biosignal-session / biosignal-analytics: recording and analysis (not yet on npm) - @elata-biosciences/app-payments: in-app purchases (Elata checkout) - @elata-biosciences/app-state: per-user key-value storage (not yet on npm; use the elata:state:* messages) - @elata-biosciences/app-metrics: event records, scores, reportAffect ## Elata rules - The build must actually use an Elata sensor SDK (rPPG, EEG, or PPG), or upload fails. - Ship a zip with index.html at the root; use relative asset URLs. - Never handle wallets, payments, or sessions in the app. Use app-payments for purchases. - Never send raw camera frames, audio, or raw biosignals off the device. - Never access window.parent beyond postMessage. - Put the release version in public/elata.json: { "version": "x.y.z" }. ## Commands - npm run dev start the dev server - npm run build:zip build and write app.zip for upload ``` *** ## Useful prompts ### Start a new app ```text theme={null} Scaffold a new camera heart-rate app with npm create @elata-biosciences/elata-demo my-app -- --template rppg and explain the project structure. ``` ### Add the SDK to an existing app ```text theme={null} Add camera heart rate with @elata-biosciences/rppg-web. Use createRppgSession, show signal quality, and stop the camera when the component unmounts. Read node_modules/@elata-biosciences/rppg-web/llms.txt first. ``` ### Add a purchase ```text theme={null} Add a "Pro theme" unlock with @elata-biosciences/app-payments. Gate the UI on getOwnedItems(), branch on result.status (not txHash), and re-apply owned benefits on startup. ``` ### Save progress ```text theme={null} Persist the player's settings and best score with Elata's elata:state:get/set messages. Fall back to localStorage when Elata replies with not_authenticated. ``` ### Prepare an upload ```text theme={null} Check that this build is ready for Elata: index.html at the zip root, relative asset paths, SDK usage present, and public/elata.json with the version from package.json. Then run npm run build:zip. ``` *** ## Tips Point the assistant at a package's `llms.txt` in `node_modules` before asking it to write integration code. These files are written for AI tools and list the supported APIs and common mistakes. * Start from a `create-elata-demo` template, then iterate. * Test in Elata's play window before submitting. The sandbox behaves differently from `localhost`. *** ## Next How publishing works The Elata SDK packages # Cursor Source: https://docs.elata.bio/apps/ai-tools/cursor Use Cursor to build Elata apps with the Elata SDK and Elata APIs. ## Overview Cursor works best on Elata apps when it knows how Elata works: the sandbox, the SDK packages, and the upload rules. This page gives you a project context file to paste in, plus prompts for common tasks. *** ## Setup 1. Install Cursor from [cursor.com](https://cursor.com). 2. Open your project. 3. Add a project rule at `.cursor/rules/elata.mdc` with the context below. *** ## Project context Save this as a project rule at `.cursor/rules/elata.mdc`: ```markdown .cursor/rules/elata.mdc theme={null} # Elata app This is a browser app for Elata (https://app.elata.bio), built on the Elata SDK. It is a static web build that runs inside a sandboxed iframe in Elata. ## Docs - Docs home: https://docs.elata.bio (full text for agents: https://docs.elata.bio/llms-full.txt) - Building apps: https://docs.elata.bio/apps/build/overview - Host bridge (Elata APIs): https://docs.elata.bio/apps/platform/host-bridge - SDK overview: https://docs.elata.bio/sdk/overview - Package references for agents: node_modules/@elata-biosciences//llms.txt ## Packages - @elata-biosciences/rppg-web: camera heart rate (use createRppgSession) - @elata-biosciences/eeg-web + eeg-web-ble: EEG over Web Bluetooth (Muse built in) - @elata-biosciences/ppg-web: headband PPG heart rate and HRV - @elata-biosciences/biosignal-session / biosignal-analytics: recording and analysis (not yet on npm) - @elata-biosciences/app-payments: in-app purchases (Elata checkout) - @elata-biosciences/app-state: per-user key-value storage (not yet on npm; use the elata:state:* messages) - @elata-biosciences/app-metrics: event records, scores, reportAffect ## Elata rules - The build must actually use an Elata sensor SDK (rPPG, EEG, or PPG), or upload fails. - Ship a zip with index.html at the root; use relative asset URLs. - Never handle wallets, payments, or sessions in the app. Use app-payments for purchases. - Never send raw camera frames, audio, or raw biosignals off the device. - Never access window.parent beyond postMessage. - Put the release version in public/elata.json: { "version": "x.y.z" }. ## Commands - npm run dev start the dev server - npm run build:zip build and write app.zip for upload ``` *** ## Useful prompts ### Start a new app ```text theme={null} Scaffold a new camera heart-rate app with npm create @elata-biosciences/elata-demo my-app -- --template rppg and explain the project structure. ``` ### Add the SDK to an existing app ```text theme={null} Add camera heart rate with @elata-biosciences/rppg-web. Use createRppgSession, show signal quality, and stop the camera when the component unmounts. Read node_modules/@elata-biosciences/rppg-web/llms.txt first. ``` ### Add a purchase ```text theme={null} Add a "Pro theme" unlock with @elata-biosciences/app-payments. Gate the UI on getOwnedItems(), branch on result.status (not txHash), and re-apply owned benefits on startup. ``` ### Save progress ```text theme={null} Persist the player's settings and best score with Elata's elata:state:get/set messages. Fall back to localStorage when Elata replies with not_authenticated. ``` ### Prepare an upload ```text theme={null} Check that this build is ready for Elata: index.html at the zip root, relative asset paths, SDK usage present, and public/elata.json with the version from package.json. Then run npm run build:zip. ``` *** ## Tips Point the assistant at a package's `llms.txt` in `node_modules` before asking it to write integration code. These files are written for AI tools and list the supported APIs and common mistakes. * Start from a `create-elata-demo` template, then iterate. * Test in Elata's play window before submitting. The sandbox behaves differently from `localhost`. *** ## Next How publishing works The Elata SDK packages # Windsurf Source: https://docs.elata.bio/apps/ai-tools/windsurf Use Windsurf to build Elata apps with the Elata SDK and Elata APIs. ## Overview Windsurf works best on Elata apps when it knows how Elata works: the sandbox, the SDK packages, and the upload rules. This page gives you a project context file to paste in, plus prompts for common tasks. *** ## Setup 1. Install Windsurf from [windsurf.com](https://windsurf.com). 2. Open your project. 3. Add a `.windsurfrules` file at your project root with the context below. *** ## Project context Save this as `.windsurfrules` at your project root: ```markdown .windsurfrules theme={null} # Elata app This is a browser app for Elata (https://app.elata.bio), built on the Elata SDK. It is a static web build that runs inside a sandboxed iframe in Elata. ## Docs - Docs home: https://docs.elata.bio (full text for agents: https://docs.elata.bio/llms-full.txt) - Building apps: https://docs.elata.bio/apps/build/overview - Host bridge (Elata APIs): https://docs.elata.bio/apps/platform/host-bridge - SDK overview: https://docs.elata.bio/sdk/overview - Package references for agents: node_modules/@elata-biosciences//llms.txt ## Packages - @elata-biosciences/rppg-web: camera heart rate (use createRppgSession) - @elata-biosciences/eeg-web + eeg-web-ble: EEG over Web Bluetooth (Muse built in) - @elata-biosciences/ppg-web: headband PPG heart rate and HRV - @elata-biosciences/biosignal-session / biosignal-analytics: recording and analysis (not yet on npm) - @elata-biosciences/app-payments: in-app purchases (Elata checkout) - @elata-biosciences/app-state: per-user key-value storage (not yet on npm; use the elata:state:* messages) - @elata-biosciences/app-metrics: event records, scores, reportAffect ## Elata rules - The build must actually use an Elata sensor SDK (rPPG, EEG, or PPG), or upload fails. - Ship a zip with index.html at the root; use relative asset URLs. - Never handle wallets, payments, or sessions in the app. Use app-payments for purchases. - Never send raw camera frames, audio, or raw biosignals off the device. - Never access window.parent beyond postMessage. - Put the release version in public/elata.json: { "version": "x.y.z" }. ## Commands - npm run dev start the dev server - npm run build:zip build and write app.zip for upload ``` *** ## Useful prompts ### Start a new app ```text theme={null} Scaffold a new camera heart-rate app with npm create @elata-biosciences/elata-demo my-app -- --template rppg and explain the project structure. ``` ### Add the SDK to an existing app ```text theme={null} Add camera heart rate with @elata-biosciences/rppg-web. Use createRppgSession, show signal quality, and stop the camera when the component unmounts. Read node_modules/@elata-biosciences/rppg-web/llms.txt first. ``` ### Add a purchase ```text theme={null} Add a "Pro theme" unlock with @elata-biosciences/app-payments. Gate the UI on getOwnedItems(), branch on result.status (not txHash), and re-apply owned benefits on startup. ``` ### Save progress ```text theme={null} Persist the player's settings and best score with Elata's elata:state:get/set messages. Fall back to localStorage when Elata replies with not_authenticated. ``` ### Prepare an upload ```text theme={null} Check that this build is ready for Elata: index.html at the zip root, relative asset paths, SDK usage present, and public/elata.json with the version from package.json. Then run npm run build:zip. ``` *** ## Tips Point the assistant at a package's `llms.txt` in `node_modules` before asking it to write integration code. These files are written for AI tools and list the supported APIs and common mistakes. * Start from a `create-elata-demo` template, then iterate. * Test in Elata's play window before submitting. The sandbox behaves differently from `localhost`. *** ## Next How publishing works The Elata SDK packages # App Lifecycle Source: https://docs.elata.bio/apps/app-lifecycle How an app goes from upload to a live, publicly playable listing. ## Overview Every app version you publish is a **release**. A release moves through one pipeline: you upload it, submit it, Elata scans it, and a moderator approves it. ``` DRAFT ──submit──▶ SUBMITTED ──scan + review──▶ APPROVED (live) │ └──▶ REJECTED (fix and resubmit) ``` *** ## Step 1: Upload Upload a `.zip` (or a single `.html` file) from the wizard or your app's edit page. Elata checks it straight away: * the file type and size are within [limits](/apps/build/upload-and-hosting#limits) * the bundle has an `index.html` entry and safe file paths * the bundle uses an Elata sensor SDK If something is missing you get a clear "App requirements not met" message listing what to fix. A successful upload becomes a **draft** release, and its version is read from your [`elata.json`](/apps/build/elata-json). You can try a draft in Elata's play window, and share it with up to 10 playtest friends, before anyone else can see it. *** ## Step 2: Submit for review When you submit, Elata checks that your listing is complete. You need an **app icon** and a **header image**. Screenshots are optional but recommended. The listing details are frozen onto the release at this point, so later edits don't change what was reviewed. You can withdraw a submission while it is pending. *** ## Step 3: Automated security scan Every submitted release is scanned automatically. The scan looks for things like wallet-draining code, attempts to escape the sandbox, credential access, obfuscated code, and missing SDK usage. A release that fails the scan is held back from Elata. Scan findings are reviewed by the Elata team and are not shown in detail to uploaders. If your release is held, you'll hear from us through Elata. *** ## Step 4: Moderator approval An Elata moderator reviews the release against the [submission policy](/apps/build/review-and-policy). Once approved, it becomes your app's live release and you're notified in Elata and by email. If it's rejected, fix the issues and submit again. *** ## After you're live Approval makes a release eligible to be played. Whether people can find and play it also depends on: | Setting | Who controls it | Values | | - | - | - | | **Visibility** | You | Public, Unlisted, Private | | **Moderation** | Elata | Active, Suspended, Banned | | **Discovery** | Elata | How prominently the app is ranked. New apps start with limited exposure. | To ship an update, upload a new build. It goes through the same pipeline, and your current live release stays up until the new one is approved. *** ## Next What every upload needs The wizard, step by step What reviewers look for # App Listing Source: https://docs.elata.bio/apps/build/app-metadata The fields and images that make up your app's page on Elata. ## Listing fields You fill these in during the wizard's Publish step and can change them any time from your app's **Edit** page. | Field | Required to submit | Limits | | - | - | - | | Name | Yes | Up to 50 characters. Set when you register. | | App icon | Yes | Square image | | Header image | Yes | Wide banner shown at the top of your app page | | Description | Recommended | 10 to 1,000 characters. Shown on app cards. | | Extended description | No | Up to 5,000 characters. Shown on your app page. | | Category | Yes | Games, Neurometrics, Wellness, Tools, or Experiments | | Tags | No | Up to 5 tags, each up to 30 characters | | Screenshots | No | Up to 10 images | | Social links | No | Website, X/Twitter, Discord, GitHub, Telegram, Farcaster, YouTube, and more, plus up to 5 custom links | | Team | No | Team members shown on your app page | When you submit a release, the listing is frozen onto that release. Edits after that apply to your next release. *** ## Image guidelines | Asset | Recommended size | Formats | | - | - | - | | App icon | 512×512 px | PNG, JPG, WebP | | Header image | 1920×600 px | PNG, JPG, WebP | | Desktop screenshot | 1920×1080 px (16:9) | PNG, JPG, WebP | | Mobile screenshot | 1080×1920 px (9:16) | PNG, JPG, WebP | Image uploads are limited to 5 MB each. Your icon is also used for home-screen installs when your bundle doesn't ship its own web manifest. Use a square icon that reads well at small sizes. *** ## Your app's URLs | URL | What it is | | - | - | | `app.elata.bio/app/` | Your app page | | `app.elata.bio/app//play` | Where users play your app | | `app.elata.bio/app/.md` | A plain-text Markdown version of your listing, useful for sharing with AI tools | *** ## Next What reviewers check What to do once you're live # elata.json Source: https://docs.elata.bio/apps/build/elata-json The app manifest that declares your release version. ## Overview `elata.json` is a small manifest you include in your upload. Today Elata uses it to read your **release version**, so your listing shows `v1.2.3` instead of `Release #4`. ```json elata.json theme={null} { "version": "1.2.3", "entry": "index.html", "name": "Calm Runner" } ``` `elata.json` is optional today. If it is missing or invalid, Elata falls back to other version sources (see below). It will become required in a future update, so add it now. *** ## Fields | Field | Required | Rules | | - | - | - | | `version` | Yes | 1 to 32 characters. Starts with a letter or digit, then letters, digits, `.`, `-`, or `+`. Semver like `1.2.3` or `1.2.3-beta.1` is recommended. | | `entry` | No | The entry HTML file, up to 256 characters. Use `index.html`. | | `name` | No | A display name, 1 to 80 characters. | *** ## File name and location * Name the file `elata.json` (`elata.manifest.json` also works). * Put it at the root of your zip, next to `index.html`. * If there are several, the one closest to the root wins. With Vite, put it in `public/` so it's copied into `dist/` on every build: ``` my-app/ ├── public/ │ └── elata.json ├── src/ └── index.html ``` *** ## Version fallbacks If there's no valid `elata.json`, Elata looks for a version in this order: 1. `elata.json` (or `elata.manifest.json`) 2. The `version` in the shallowest `package.json` in the zip 3. A meta tag in `index.html`: ```html theme={null} ``` The upload response tells you which source was used. # Publish Your App Source: https://docs.elata.bio/apps/build/launch-your-app Step-by-step through the Build an App wizard, from registration to submitting for review. ## Overview Publishing happens in the **Build an App** wizard at [app.elata.bio/build-app](https://app.elata.bio/build-app). It has four steps. Your progress is saved as a draft, so you can leave and come back. ``` Build → Register → Upload → Publish ``` *** ## 1. Build The first step is a checklist. Before you continue you should have: * an app built with an Elata SDK and tested locally * a production `.zip` ready to upload If you scaffolded with `create-elata-demo`, run: ```bash theme={null} npm run build:zip ``` This builds the app and writes `app.zip` with `index.html` at its root, which is the format Elata expects. *** ## 2. Register Pick your app's name (up to 50 characters). Elata checks that the name is available and gives your app a short URL slug, used in `app.elata.bio/app/`. Then pay the one-time registration fee in USDC. The amount is shown in the wizard. *** ## 3. Upload Upload your `app.zip`. Elata validates it immediately: file type, size, an `index.html` entry, safe paths, and SDK usage. If anything is missing you'll see exactly what to fix. Once it passes, try your app in the play window. This is how users will see it. Add an [`elata.json`](/apps/build/elata-json) to your build so the release shows your version number. *** ## 4. Publish Fill in your Elata listing: * **App icon** (required) * **Header image** (required) * **Description**, category, and tags * **Screenshots** (optional, up to 10) Then click **Submit for Review**. Your release goes through the automated security scan and moderator review described in [App Lifecycle](/apps/app-lifecycle). *** ## After submitting * Track the review status on your app page and in Elata's notifications. * Invite up to 10 playtest friends to try the draft before it's approved. * Ship updates later from your app's **Edit** page. Each new upload creates a new release. *** ## Next Fields and image guidelines Limits, self-hosting, PWA install What to do once you're live # Monetize Your App Source: https://docs.elata.bio/apps/build/monetize Configure products and subscriptions, track sales, and access your proceeds. Manage your app's monetization from **My Apps → your app → Edit → Commerce**. This section contains products, the monthly subscription plan, revenue, recent transactions, and payout-wallet funds. Payments must be enabled and configured in the deployment. See [What's live](/overview/status). The app checkout and Funds flow described here uses USDC on Base. Verify the checkout network before making a test purchase. ## Choose what to sell | Model | Suitable for | What you build | | - | - | - | | [In-app purchases](/apps/platform/in-app-purchases) | One-time feature, level, or cosmetic unlocks | Create products, then integrate `app-payments` and apply the purchased benefit | | [App subscription](/apps/platform/subscriptions) | Monthly access to the whole app | Configure a monthly plan; Elata handles the access gate | You can offer both. An app subscription provides app access, while item ownership remains separate. A single item identifier does not support repeated consumable purchases. ## Set up monetization Open **Commerce → Funds** and inspect the payout wallet. Direct app payments use the app creator's wallet as their destination. Confirm you control it before offering paid content. Adding team members does not create payout shares. Under **Products**, create a title, description, price, optional image, and active state. Elata assigns the stable `contentId` your app uses. Under **Monthly subscription**, set a positive monthly USDC price, trial length, and active state. Each section has its own save action. For items, read the catalog and ownership through `app-payments`, request checkout, and apply the benefit after success. Restore owned benefits when the app loads. See [In-app purchases](/apps/platform/in-app-purchases) for the integration. Use a separate user account to test a purchase or subscription. Owners and team members bypass the app subscription gate. Test signed-out, wallet connection, cancellation, owned-item, trial, paid-period, and expired-access states as applicable. Review **Revenue** and **Transactions**, then use **Funds → Manage funds** to access the payout wallet. Follow [Revenue and payouts](/apps/operate/revenue-and-payouts) to send USDC or move it to a service with bank withdrawals. ## How money reaches you The buyer pays USDC through the splitter. The splitter deducts the configured platform fee and routes your share directly to the payout wallet. Elata does not keep a separate creator balance that you later claim. The Funds card displays the wallet's current USDC balance on Base. It can include other deposits and proceeds from other apps using that wallet, and it decreases when you send funds out. It is not an app-specific earnings total. ## Fees and reporting Registration has a one-time fee shown in the registration wizard. Direct app sales have a configured platform fee deducted by the splitter. Network and service fees may also apply. This guide does not fix a percentage: use the active payment configuration and transaction records. Commerce reports show **gross** amounts before the split. Product revenue is estimated using current product prices; subscription payments retain their recorded payment amounts. Do not use the gross estimate as your net payout balance. ## Credits and points User credits cover supported metered features; they are not your app's payout balance. Points are also separate from USDC revenue. ## Next steps Create items and connect checkout to app benefits. Configure monthly app access and trials. Read your reports and get your money out. Verify the published customer experience. # Builder Overview Source: https://docs.elata.bio/apps/build/overview What Elata gives you and what you avoid building yourself. The repository's `create-elata-demo` 0.12.1 has a packaging blocker: missing `app-metrics` version metadata prevents startup. Check [scaffolder status](/sdk/create-elata-demo) before using the commands below; for now, follow [manual rPPG integration](/sdk/tutorials/rppg-existing-app). ## What You Get You ship a static web app. Elata provides the rest: * Hosting for your build, or embedding of your own HTTPS site * an Elata listing with discovery, reviews, comments, votes, and sharing * Signed-in users, with no auth code in your app * One-time in-app purchases and monthly subscriptions, paid in USDC * Per-user key-value storage, event records, and score leaderboards * A consent-gated, cross-app biometric Score * Foreground reminders and an in-app feedback channel *** ## What You Avoid Building Yourself | You would otherwise need | Elata handles it | | - | - | | Auth, sessions, wallets | Users sign in to Elata. Your app does not receive their account credentials or session tokens through the bridge. | | Checkout and payment processing | Elata runs checkout and records ownership. You read entitlements. | | A backend for saves and settings | [`app-state`](/apps/platform/app-state) and [`app-metrics`](/apps/platform/metrics-and-scores) store per-user data for you. | | Consent flows for biometric data | Elata shows consent in its own trusted frame and enforces it server-side. | | Hosting, CDN, TLS | Upload a `.zip`. Elata serves it on an isolated per-app origin. | *** ## How Your App Runs Your app runs inside a sandboxed iframe on its own origin. It has no access to Elata's cookies, the user's wallet, or Elata's backend. Everything it needs from the platform goes through a small `postMessage` bridge that the `@elata-biosciences/app-*` packages wrap for you. See [Host bridge](/apps/platform/host-bridge). *** ## Build Steps Start from a template with `npm create @elata-biosciences/elata-demo my-app`, or add the SDK to an existing app. See [Your first app](/sdk/tutorials/first-app). Make sure your build meets the [requirements](/apps/build/requirements), including real SDK usage and an `index.html` entry. Declare your release version in an [`elata.json`](/apps/build/elata-json) manifest. Walk through the [Build an App wizard](/apps/build/launch-your-app). *** ## Next What your app needs The wizard, step by step How your app talks to Elata # Post-Launch Checklist Source: https://docs.elata.bio/apps/build/post-launch-checklist What to do once your app is live on Elata. ## Right after approval Open `app.elata.bio/app/` and confirm the icon, header, description, and screenshots look right on desktop and mobile. Run a full session from the play page while signed in, and again signed out, so you see what new users see. Add team members and social links from the **Edit** page so users can find your community. Keep the app **Public** to appear in discovery, or **Unlisted** to share it only by link. *** ## Before accepting payments * **Check the payout address** in **Edit → Commerce → Funds** and confirm you control that wallet. * **Save products and plans** using their own save actions in Commerce. Confirm active products have the right identifiers and prices. * **Test as a separate user**, because owners and team members bypass app subscription gating. * **Verify item restoration** after reload and when an item is deactivated. * **Check subscription dates**, trial eligibility, current-month coverage, next-month renewal, and expired access. * **Exercise payment states**, including signed-out, no wallet, insufficient funds, cancellation, and unavailable checkout. * **Verify a completed payment** in user Billing, creator Commerce, and the payout wallet. Check the network before using funds in a preview. * **Make your offer clear**: items are one-time unlocks; app subscriptions renew manually; subscription access does not include all paid items. Follow [Monetize your app](/apps/build/monetize) for setup and [Revenue and payouts](/apps/operate/revenue-and-payouts) to access the proceeds. *** ## Launch week * **Share your app link.** Shared links are tracked, and visits from them count toward the sharer's [Elata Points](/overview/points). * **Reply to reviews and comments** on your app page. * **Add an in-app feedback entry** so users can reach you from inside the app. See [Host bridge](/apps/platform/host-bridge#feedback). * **Watch your analytics** in **Edit → Analytics**. *** ## Ongoing * Ship improvements as new releases. Each goes through review, and your current release stays live until the new one is approved. * Keep your changelog accurate, especially for changes in monetization or data use. * Consider [in-app purchases](/apps/platform/in-app-purchases) or a [subscription plan](/apps/platform/subscriptions). *** ## Next Growth playbook What to track # Requirements Source: https://docs.elata.bio/apps/build/requirements What your app needs before you upload and submit it. ## Checklist Sign in at [app.elata.bio](https://app.elata.bio). You can sign in with a social login or connect an existing wallet. Registering an app name costs a small one-time fee in USDC. The current amount is shown in the wizard. Uploads are rejected if no Elata SDK usage is detected. Use camera rPPG (`rppg-web`), EEG over Bluetooth (`eeg-web` + `eeg-web-ble`), or headband PPG (`ppg-web`, which runs on the EEG transport). A `.zip` with `index.html` at its root (or a single `.html` file). Asset paths must be **relative**, because apps are served under a sub-path. See [Upload and hosting](/apps/build/upload-and-hosting). Add an [`elata.json`](/apps/build/elata-json) so Elata shows `v1.2.3` instead of a release number. An app icon and a header image are required to submit. Screenshots and an extended description are recommended. See [App listing](/apps/build/app-metadata). *** ## SDK usage check Elata looks for real calls into the Elata SDK in your bundle, for example `createRppgSession(...)` from `rppg-web`, or `initEegWasm(...)` and `BleTransport` from the EEG packages, along with the packaged WASM files. Importing a package without using it is not enough. The templates from `npm create @elata-biosciences/elata-demo` already pass this check. If you integrate the SDK by hand, keep the packaged `.wasm` files in your build output. *** ## Things your app must not do Apps run in a sandbox. Reviews and the automated scan reject apps that: * start wallet transactions, signatures, or approvals outside Elata's own checkout * send raw camera frames, microphone audio, or raw biosignals off the device * try to reach the parent page, its cookies, or storage, or escape the sandbox * impersonate another app, developer, or service The full list is in [Review and policy](/apps/build/review-and-policy). *** ## Next Walk through the wizard The app manifest # Review and Policy Source: https://docs.elata.bio/apps/build/review-and-policy What the automated scan and moderators check before an app goes live. ## How review works Every release is checked twice before it goes live: 1. **Automated security scan.** Elata scans your bundle for known malicious patterns, wallet-draining code, sandbox-escape attempts, obfuscated code, and missing SDK usage. A model-assisted code review runs alongside the pattern checks. 2. **Moderator review.** An Elata moderator reviews the release, its listing, and the scan results, then approves or rejects it. A release that fails the scan is held back and can't be approved until it's resolved. To keep the scan effective, detailed security findings aren't returned to uploaders. Upload-time problems you *can* fix yourself, such as a missing `index.html` or no SDK usage, are always shown to you directly. *** ## The submission policy The full text is published at [elata.bio/terms#developer-app-submission](https://elata.bio/terms#developer-app-submission). In short, your app must not: No token transfers, signatures, or approvals except through Elata's own checkout. No unlimited spending approvals, and no disguising what a transaction does. Raw biometric data, camera frames, and microphone audio must not leave the user's device or go to a third party. Derived, on-device results (for example a calm score) are fine if disclosed to the user. The one first-party exception is sending derived session results to Elata through [`reportAffect`](/apps/platform/metrics-and-scores#reportaffect), with the user's consent. No copying another app's or company's name, branding, icons, or domain. No attempts to reach the parent page, bypass the frame's isolation, or work around Elata's security controls. Please report sandbox defects through responsible disclosure instead. Name, description, screenshots, and changelog must accurately describe the app. Material changes in behavior or monetization belong in your release changelog. Violations can lead to a release being removed, or an app or developer account being suspended or banned. *** ## Tips for a smooth review * Keep your bundle readable. Heavily obfuscated code draws extra scrutiny. * Only request camera or Bluetooth access when the user starts a session. * Make sure the app works in Elata's play window before submitting. * Describe in your listing what the app measures and what it does with the data. # Upload and Hosting Source: https://docs.elata.bio/apps/build/upload-and-hosting Upload limits, self-hosted URLs, the sandbox your app runs in, and home-screen install. ## Two ways to host | Option | How it works | When to use it | | - | - | - | | **Elata-hosted** (default) | Upload a `.zip` or a single `.html` file. Elata serves it. | Almost always | | **Self-hosted** | Give Elata a public `https://` URL. Elata embeds it. | Apps that need their own server. Requires approval from Elata. | Uploaded code is treated as static web content (HTML, CSS, JS, and assets). It runs in the user's browser and is never executed on Elata's servers. *** ## Limits | Limit | Value | | - | - | | Upload size | 200 MB | | Unzipped size | 500 MB | | Files in a zip | 2,000 | | Single file size | 100 MB | The zip must contain `index.html`. File paths must be relative and plain: no absolute paths, no `..` segments, and no control characters. *** ## Building for Elata * **Use relative URLs.** Apps are served under a sub-path, not at the domain root. Load assets and data with relative paths (`./data.json`, not `/data.json`). * **Ship the WASM files.** Keep the SDK's packaged `.wasm` files in your build output. * **Use Elata's bridge, not browser storage, for anything that matters.** Each app runs on its own origin, so `localStorage` works but stays on one device. Use [`app-state`](/apps/platform/app-state) for data that should follow the user. * **Ask for camera access only when needed.** rPPG apps run in the Elata frame and the browser will prompt for camera permission. *** ## The sandbox When someone plays your app, Elata loads it in a sandboxed iframe on an isolated origin for your app. In that sandbox your app: * **can** run scripts, use the camera (with permission), use Web Bluetooth in supported browsers, use pointer lock, and use its own storage * **cannot** read Elata's cookies or storage, see the user's wallet or session, or navigate the Elata page To talk to Elata, use the [host bridge](/apps/platform/host-bridge). *** ## Self-hosted apps To use your own URL, request external hosting from your app's **Edit** page. Once Elata approves the request you can publish releases that point at an `https://` URL. Self-hosted URLs must: * use `https://` and be publicly reachable * allow being embedded in a frame (don't send `X-Frame-Options: DENY` or a CSP `frame-ancestors` that excludes `app.elata.bio`) Self-hosted URLs are also checked against a URL-reputation service. *** ## Home-screen install (PWA) When someone installs your app from its play page (**Add to Home Screen**), Elata makes the install use **your** app's name and icon if your bundle ships a web manifest: 1. Add `manifest.webmanifest` (or `manifest.json`) next to `index.html`. 2. Link it from your HTML: ``. Use relative paths. 3. Include at least one icon. A 512×512 maskable PNG works best. 4. Set `name`, `short_name`, `theme_color`, and `background_color`. Elata manages `start_url`, `scope`, and `display` so the installed app reopens correctly. Without a manifest, the install uses your Elata listing's name and image. *** ## Next How your app talks to Elata Icon, header, screenshots # Choose Your Path Source: https://docs.elata.bio/apps/choose-your-path Whether you want to build an app or use one, start here. ## For Builders You want to publish an app on Elata. What Elata gives you and what you avoid building What your app needs before you submit Step-by-step through the Build an App wizard Purchases, storage, scores, consent, and notifications *** ## For Users You want to discover and play apps. Find, play, and save apps Manage app purchases and subscriptions Earn points for taking part *** ## Not Sure? Start with [What Are Elata Apps?](/apps/what-are-elata-apps) for the big picture, or jump to [What You Can Build](/apps/what-you-can-build) for examples. # Analytics Source: https://docs.elata.bio/apps/operate/analytics-and-health The usage analytics Elata gives you, and how to get the most from them. ## Your analytics page Open **My Apps → your app → Edit → Analytics** while signed in as the app's owner or a team member. The earlier `/app//analytics` route redirects to the Analytics section of the edit console. It shows aggregate usage for a time window (90 days by default): | Metric | What it counts | | - | - | | **Users** | Distinct players who recorded data in your app | | **Sessions** | Score submissions through `saveScore`, rather than independently counted play sessions | | **Events** | All stored `app-metrics` rows in the window, including event records and score rows | | **Avg score** | The average of scores saved with `saveScore` | A usage chart shows the trend over the window. *** ## Privacy built in Analytics are aggregate and anonymized: * No individual users or raw payloads are ever shown. * Results are suppressed below five distinct users, so very new apps may show no data at first. * Totals and trends are computed from at most 20,000 recent rows; the distinct-user count is computed separately. Treat very high-volume totals as potentially partial. *** ## Make your analytics useful Analytics are built from what your app records through [`app-metrics`](/apps/platform/metrics-and-scores). To get meaningful numbers: * **Record meaningful events**, for example `session_complete` or `level_complete`, not every frame. * **Save scores** with `saveScore` when a session produces a result. This drives **Avg score** and the current **Sessions** count. Multiple score submissions in one play session increase that count multiple times. * **Keep event types stable** across releases so trends stay comparable. *** ## What to watch | Signal | What it may mean | | - | - | | Users rising, sessions per user flat | New players arrive but don't return. Look at onboarding and saved progress. | | Sessions rising, avg score rising | Players are getting better. Consider harder content. | | Drop after a release | Check the new build in the play window and read recent reviews. | Your app page also shows plays, saves, votes, reviews, and comments. *** ## Compare usage with sales Open **Edit → Commerce** for product sales, subscription payments, and recent transactions. Use **Edit → Analytics** to understand usage. These panels answer different questions: paid access does not guarantee someone played, and trial access does not imply a direct app payment. Usage analytics remain aggregate and subject to the minimum-cohort rule. Commerce transaction records are a separate operational view and may display shortened buyer wallet addresses. Product revenue estimates use current catalog prices; subscription revenue uses recorded payment amounts. Neither is a net wallet balance. See [Revenue and payouts](/apps/operate/revenue-and-payouts) for interpretation and access to funds. # Grow Your Community Source: https://docs.elata.bio/apps/operate/grow-your-community A practical playbook for getting players to your app and keeping them. ## Launch week The first week sets the tone. Focus on getting people to play, and making it easy for them to talk to you. 1. **Share your Elata link everywhere.** Post `app.elata.bio/app/` on X, Discord, and relevant communities. Shared links are tracked, so people who share your app earn [Elata Points](/overview/points) when others click through. 2. **Fill in your listing.** A clear header image, screenshots, and an extended description noticeably improve conversion. See [App listing](/apps/build/app-metadata). 3. **Add your team and social links** so players can see who's behind the app. 4. **Put a feedback button inside your app** using Elata's feedback channel. See [Host bridge](/apps/platform/host-bridge#feedback). *** ## Keep players coming back * **Save progress** with [`app-state`](/apps/platform/app-state) so players pick up where they left off, on any device. * **Show progress over time.** Record sessions and scores with [`app-metrics`](/apps/platform/metrics-and-scores) and show personal bests or trends. * **Use reminders sparingly.** Elata-brokered [notifications](/apps/platform/notifications) are opt-in and rate-limited. Use them for things the player asked for, such as "session complete". * **Ship regular updates** and describe them in your release changelog. * **Reply to reviews and comments** on your app page. *** ## Discovery Elata ranks apps using signals such as plays, saves, votes, reviews, and recency, and personalizes recommendations for signed-in users. New apps start with limited exposure and gain visibility as people engage with them. Users can also: * **save** your app to their collection * **vote** and **review** it * **follow** you and other players * share it with a tracked link *** ## Social presence | Platform | Use it for | | - | - | | X / Twitter | Announcements and release updates | | Discord | Support, feedback, and community | | GitHub | Open source code and issues | | Website | A landing page outside Elata | *** ## Next What to track Monetize with one-time items # Revenue and Payouts Source: https://docs.elata.bio/apps/operate/revenue-and-payouts Understand your sales reports, access your payout wallet, and move your USDC. Your share of direct app sales goes to your app's payout wallet after the splitter deducts the configured platform fee. You access that wallet to move the money; there is no separate Elata creator balance to withdraw. ## Open your commerce dashboard Go to **My Apps → your app → Edit → Commerce**, at `/app//edit#commerce` on the appstore. | Panel | What it shows | | - | - | | Products | Your catalog and active state | | Monthly subscription | The app's price, trial settings, active state, and subscriber signal | | Revenue | Product sales and estimated product revenue, recorded subscription payments, and estimated total gross revenue | | Transactions | Recent product and subscription records, with transaction links when available | | Funds | The payout address and that wallet's current USDC balance on Base | ## Understand the numbers **Product revenue is estimated.** Product purchase records do not store a historical paid amount. The report multiplies purchase counts by each item's current price. Editing a price can change the estimate for earlier sales without changing what anyone actually paid. **Subscription revenue uses recorded amounts.** Paid periods store the amount charged at payment time. Free trials are not paid subscription revenue. **Revenue is gross.** The report does not calculate your exact net earnings after platform fees. If it says the source records were limited, totals may also be partial. Use transaction records to reconcile actual payments and recipient amounts. **Funds is a wallet balance.** It includes other USDC deposited in that wallet and excludes funds already sent out. Several apps can share a wallet. A zero balance does not mean the app has never earned revenue. ## Where payouts go The current app payment destination is the app creator's wallet, displayed as the payout wallet in **Funds**. Products and the monthly plan do not have their own editable payout-address field. Before selling, compare the displayed address with the wallet you control. If it is wrong or inaccessible, resolve it with [support](/users/help) before accepting payments. Adding a team member does not transfer ownership or configure automatic revenue sharing. ## Access your funds In **Commerce**, find **Funds**, check the full payout address, and read **USDC balance (Base)**. Refresh or retry if the balance cannot be loaded. Sign in as the app owner and connect the wallet associated with the payout address. The console offers **Manage funds** only when its owner and wallet checks pass. A team member can view the report but does not get this owner-only action. This opens wallet controls. Available send, receive, and export options depend on the wallet type. Check the wallet address inside the opened interface against the payout address before transferring anything. The dashboard's permissions control its own buttons. Anyone who independently controls the payout wallet can also manage that wallet outside Elata. ## Send USDC to another wallet 1. Open the payout wallet, using **Manage funds** or your wallet's own application. 2. Select **USDC on Base** and the send action. 3. Enter the destination address and amount. Confirm that the destination accepts USDC on Base. 4. Review any network fee or sponsorship details and confirm the transfer. 5. Save the transaction hash and check the destination balance after confirmation. An external wallet may require ETH on Base for network fees. Embedded or smart wallets may handle fees differently; use the details shown by the wallet. Export, when supported, exports wallet access rather than converting USDC to cash. ## Get money into a bank account Elata's Funds card does not provide a bank withdrawal or convert USDC to fiat. To cash out: 1. Use an exchange or cash-out service available to you that explicitly accepts **USDC deposits on Base** and offers withdrawals to your bank. 2. Obtain its USDC deposit address with **Base** selected as the deposit network. 3. Send USDC from your payout wallet to that address. 4. After the service credits the deposit, follow its conversion and bank withdrawal steps. Check that service's current deposit minimums, fees, verification requirements, and bank withdrawal options before sending. A deposit address for a different network is not enough. Those steps happen with the receiving service, outside Elata. ## Pay collaborators Team access lets collaborators help manage the app. It does not automatically split direct app revenue among them. The current flow sends the creator's share to one payout wallet. To pay collaborators from those proceeds, make transfers from that wallet according to your own agreed arrangement. ## Troubleshooting | Problem | What to check | | - | - | | Manage funds is missing | Owner status, wallet connection, and the displayed payout address | | The wallet opened on a different address | Close it and select the actual payout wallet before sending | | Balance does not match Revenue | Gross versus net, estimated item prices, funds already sent, other deposits, and shared wallets | | A customer's payment is missing from reports | Settlement and recording state; compare the transaction hash with the app's records | | Balance cannot be loaded | Retry the balance query; a read failure is not evidence funds are gone | | You need a payout-address change | Contact support; product and plan settings do not change the payout destination | For missing or disputed payments, keep the app identifier, date, item or paid period, and transaction hash. Use [Help](/users/help). The dashboard does not provide self-service refunds, payout-address replacement, or a bank cash-out button. ## Related guides * [Monetize your app](/apps/build/monetize) * [In-app purchases](/apps/platform/in-app-purchases) * [Subscriptions](/apps/platform/subscriptions) * [Analytics](/apps/operate/analytics-and-health) * [The user billing experience](/users/billing-and-purchases) # App State Source: https://docs.elata.bio/apps/platform/app-state Per-user, per-app key-value storage with @elata-biosciences/app-state. `@elata-biosciences/app-state` is not published to npm yet. Until it is, you can call Elata directly with the `elata:state:*` messages shown below; Elata already supports them. ## Overview `@elata-biosciences/app-state` stores small JSON values per user and per app: save games, settings, progress. You don't need a backend, and the bridge does not disclose the user's account credentials. Values follow the user across devices. ```bash theme={null} # Not on npm yet. This command works after the first release. npm install @elata-biosciences/app-state ``` ```ts theme={null} import { getState, setState, deleteState } from '@elata-biosciences/app-state'; // null if nothing was ever stored const save = await getState('save_slot_1'); await setState('save_slot_1', { level: 7, hp: 100 }); // idempotent: no error if the key was never set await deleteState('save_slot_1'); ``` *** ## Limits | Limit | Value | | - | - | | Key | 1 to 256 characters from `A-Z a-z 0-9 . _ : / -` | | Value | Any JSON-serializable value, up to 64 KB once encoded | | Scope | One namespace per (user, app). Apps can't read each other's data. | *** ## Errors All three functions reject with `AppStateError`, which has a `code`: | Code | Where it comes from | Meaning | | - | - | - | | `no_parent` | Local | Not running inside Elata's frame | | `timeout` | Local | No reply in time | | `invalid_input` | Local or Elata | Bad key or value | | `not_authenticated` | Elata | The user isn't signed in | | `value_too_large` | Elata | Value is over 64 KB | | `fetch_failed` | Elata | Elata couldn't reach storage | Players who aren't signed in get `not_authenticated`. Keep a local fallback, such as `localStorage`, and sync to `app-state` once they sign in. *** ## Without the package Elata answers these messages directly. Send them to `window.parent` with a unique `requestId` and wait for the matching `…:result` reply: ```js theme={null} function stateCall(type, payload) { return new Promise((resolve, reject) => { const requestId = crypto.randomUUID(); const onMessage = (e) => { if (e.data?.type !== `${type}:result` || e.data.requestId !== requestId) return; window.removeEventListener('message', onMessage); e.data.error ? reject(new Error(e.data.error)) : resolve(e.data); }; window.addEventListener('message', onMessage); window.parent.postMessage({ type, requestId, ...payload }, '*'); }); } const { value } = await stateCall('elata:state:get', { key: 'save_slot_1' }); await stateCall('elata:state:set', { key: 'save_slot_1', value: { level: 7 } }); await stateCall('elata:state:delete', { key: 'save_slot_1' }); ``` Add a timeout in production: outside Elata nothing replies. *** ## app-state or app-metrics? | Use | Package | | - | - | | The latest value of something: settings, a save slot, unlocked levels | `app-state` | | A history of events or scores you want to query and chart | [`app-metrics`](/apps/platform/metrics-and-scores) | # Consent and Insights Source: https://docs.elata.bio/apps/platform/consent-and-insights Ask for biometric data-sharing consent and read a user's cross-app insights. ## The biometric Score Elata keeps a cross-app **biometric Score** for each user, built from derived session results that apps report with [`reportAffect`](/apps/platform/metrics-and-scores#reportaffect). Nothing is shared until the user explicitly opts in, and they can turn it off at any time. Consent is shown by Elata in its own trusted frame, not by your app. Elata also re-checks consent on the server for every sample, so your app can't contribute without it. There's currently one consent purpose: `platform_score`. *** ## Ask for consent ```js theme={null} const requestId = crypto.randomUUID(); window.addEventListener('message', (e) => { if (e.data?.type === 'elata:consent:state' && e.data.requestId === requestId) { console.log('granted?', e.data.granted); } }); window.parent?.postMessage( { type: 'elata:consent:request', purpose: 'platform_score', requestId }, '*', ); ``` Elata opens its consent dialog and replies with `{ type: 'elata:consent:state', purpose, granted, requestId }` once the user decides. To let users **turn sharing off** from your app's settings, send the same message with `action: 'revoke'`. *** ## Read consent without prompting To show the current state in your UI, for example as a settings toggle, send `elata:consent:query`. You get the same reply, and no dialog is shown. ```js theme={null} window.parent?.postMessage( { type: 'elata:consent:query', purpose: 'platform_score', requestId: 'q1' }, '*', ); ``` *** ## Read the user's insights Users who share their Score can see their own cross-app results inside your app. Request them with `elata:insights:request`: ```js theme={null} window.parent?.postMessage( { type: 'elata:insights:request', requestId: 'i1', windowDays: 90 }, '*', ); ``` Elata replies with `elata:insights:state`: ```ts theme={null} { type: 'elata:insights:state', requestId?: string, consented: boolean, // false → no data; prompt the user to opt in data?: { engagement: { activeDays: number, currentStreak: number, longestStreak: number, appsUsed: number, totalScores: number, lastActiveAt: number | null, }, apps: { slug: string | null, name: string | null }[], score: { value: number | null, calibrating: boolean, sampleCount: number }, windowDays: number, generatedAt: number, } } ``` `windowDays` defaults to 90 and is capped at 365. The data is a curated summary of the user's **own** activity; it never includes other users or raw records. *** ## Checklist for Score-contributing apps 1. Ask the Elata team to enable the `biometrics` scope for your app. 2. Explain in your app and listing what is measured and what is shared. 3. Ask for consent with `elata:consent:request` before calling `reportAffect`. 4. Handle `scope_denied` from `reportAffect` by offering the consent prompt again. # Host Bridge Source: https://docs.elata.bio/apps/platform/host-bridge How your sandboxed app talks to Elata, and every message it supports. ## Overview Your app runs in a sandboxed iframe with no access to Elata's cookies, backend, or the user's wallet. When it needs something from the platform, such as a purchase, saved data, or consent, it sends a `postMessage` to the parent frame. Elata does the work with the user's session and replies. The bridge does not expose account credentials or session tokens. Public activity, commerce records, and information a user enters are separate from this isolation. ``` ┌──────────── app.elata.bio (Elata) ────────────┐ │ session · checkout · storage · consent UI │ │ ▲ │ │ │ │ postMessage │ reply │ │ ┌─────┴─────────────────────────▼─────────┐ │ │ │ your app (sandboxed, own origin) │ │ │ └─────────────────────────────────────────┘ │ └───────────────────────────────────────────────┘ ``` *** ## Use the SDK packages For most features there's a small package that wraps the messages, timeouts, and errors for you: | Feature | Package | Page | | - | - | - | | One-time purchases and ownership | `@elata-biosciences/app-payments` | [In-app purchases](/apps/platform/in-app-purchases) | | Per-user key-value storage | `@elata-biosciences/app-state` (not yet on npm) | [App state](/apps/platform/app-state) | | Event records, scores, `reportAffect` | `@elata-biosciences/app-metrics` | [Metrics and scores](/apps/platform/metrics-and-scores) | Consent, insights, notifications, navigation, and feedback use plain `postMessage` calls. They're documented below and on their own pages. *** ## Ground rules * **Send to the parent.** Post to `window.parent`. Use the known host origin as the target when available. SDK clients handle their own transport. Hand-written listeners should verify the replying window and, when known, its origin; do not assume every bridge handler enforces identical checks. * **Use a `requestId`.** Where a reply is expected, include a unique `requestId` (8 to 128 characters; `crypto.randomUUID()` works) and match it on the reply. * **Handle no reply.** Outside Elata (for example in local development) nobody answers. Use a timeout and degrade gracefully. The SDK packages do this for you. * **Elata is the source of truth.** Prices, ownership, and consent always come from Elata, never from values your app sends. *** ## Message reference ### Purchases | App → Elata | Elata → app | | - | - | | `elata:iap:request` `{ requestId, contentId, priceUsdc?, title?, description? }` | `elata:iap:result` `{ requestId, status: "success" \| "cancelled" \| "error", txHash?, error? }` | | `elata:iap:getCatalog` `{ requestId }` | `elata:iap:getCatalog:result` `{ requestId, items }` | | `elata:iap:hasItem` `{ requestId, contentId }` | `elata:iap:hasItem:result` `{ requestId, owned }` | | `elata:iap:listOwned` `{ requestId }` | `elata:iap:listOwned:result` `{ requestId, ownedContentIds }` | Use [`app-payments`](/apps/platform/in-app-purchases) instead of sending these by hand. ### App state | App → Elata | Elata → app | | - | - | | `elata:state:get` `{ requestId, key }` | `elata:state:get:result` `{ requestId, value }` or `{ requestId, error }` | | `elata:state:set` `{ requestId, key, value }` | `elata:state:set:result` `{ requestId, ok }` or `{ requestId, error }` | | `elata:state:delete` `{ requestId, key }` | `elata:state:delete:result` `{ requestId, ok }` or `{ requestId, error }` | Once `@elata-biosciences/app-state` is on npm, prefer it over sending these by hand. See [App state](/apps/platform/app-state). ### Metrics `app-metrics` uses a dedicated `MessageChannel` set up with a `__elata_metrics_init` handshake, not individual window messages. Always use the [package](/apps/platform/metrics-and-scores). ### Consent and insights | App → Elata | Elata → app | | - | - | | `elata:consent:request` `{ requestId?, purpose: "platform_score", action?: "grant" \| "revoke" }` | `elata:consent:state` `{ requestId?, purpose, granted }` | | `elata:consent:query` `{ requestId?, purpose: "platform_score" }` | `elata:consent:state` `{ requestId?, purpose, granted }` | | `elata:insights:request` `{ requestId?, windowDays? }` | `elata:insights:state` `{ requestId?, consented, data? }` | See [Consent and insights](/apps/platform/consent-and-insights). ### Notifications | App → Elata | Elata → app | | - | - | | `elata:notify:request` `{ requestId?, body }` | `elata:notify:state` `{ requestId?, status }` | See [Notifications](/apps/platform/notifications). ### Navigation Send the user to another app's play page in Elata: ```js theme={null} window.parent?.postMessage({ type: 'elata:navigate', slug: 'calm-runner' }, '*'); ``` `slug` must be an Elata app slug: lowercase letters, digits, and dashes, up to 64 characters. There's no reply. ### Feedback Elata's floating feedback button is hidden while an app is playing, so it doesn't cover your controls. Open Elata's feedback panel from your own UI instead: ```js theme={null} window.parent?.postMessage({ type: 'elata:feedback:open', mode: 'feature' }, '*'); // mode: 'feature' (default) or 'bug' ``` There's no reply. # In-App Purchases Source: https://docs.elata.bio/apps/platform/in-app-purchases Sell one-time items in your app with @elata-biosciences/app-payments. ## Overview `@elata-biosciences/app-payments` lets your app open Elata's checkout and check what the user owns. Elata runs checkout, takes payment in USDC, and records ownership. Your app never touches wallets or payment details. ```bash theme={null} npm install @elata-biosciences/app-payments ``` | Function | Returns | Use it to | | - | - | - | | `getCatalog(opts?)` | `Promise` | List your app's items with price, title, and description | | `getOwnedItems(opts?)` | `Promise` | List every `contentId` the user owns | | `hasItem(contentId, opts?)` | `Promise` | Check one item | | `requestPurchase(input)` | `Promise` | Open checkout for an item | ## Create your catalog Open **My Apps → your app → Edit → Commerce → Products**. Owners and authorized team members can create and manage the catalog themselves. | Field | What to enter | | - | - | | Title | A clear item name, up to 100 characters | | Description | What the buyer receives, up to 500 characters | | Image URL | Optional HTTP or HTTPS image URL | | Price | A positive human-readable USDC amount in the dashboard form | | Active | Whether the item is available in the active catalog | Save the product, then copy its assigned `contentId` into your integration. The identifier is stable and cannot be edited. Products can be edited or deactivated rather than deleted. Deactivation stops new sales; keep applying existing owners' benefits, including when the item no longer appears in the active catalog. The dashboard accepts ordinary amounts such as `1.00`. Product APIs and catalog responses use integer USDC base units: `"1000000"` means 1 USDC. Creating a catalog entry does not implement its benefit in your app. Checkout must be configured for the deployment. The app checkout described here pays USDC on Base through the splitter. See [What's live](/overview/status) before testing payments. *** ## The correct flow Gate your UI on ownership first, then purchase, then apply the benefit. ```ts theme={null} import { getCatalog, getOwnedItems, requestPurchase, AppPaymentsError, } from '@elata-biosciences/app-payments'; const formatUsdc = (base: string) => `$${(Number(base) / 1e6).toFixed(2)}`; // 1. Read the catalog and what the user already owns. const [items, owned] = await Promise.all([getCatalog(), getOwnedItems()]); const ownedSet = new Set(owned); // 2. Re-apply benefits on startup. Elata is the source of truth, // so this survives reloads and device changes. for (const id of owned) applyBenefit(id); // 3. Buy, then branch on status. async function buy(item) { try { const result = await requestPurchase({ contentId: item.contentId, priceUsdc: formatUsdc(item.priceUsdc), // display hint only title: item.title, }); if (result.status === 'success') { ownedSet.add(item.contentId); applyBenefit(item.contentId); } else if (result.status === 'error') { showError(result.error); } // 'cancelled': the user backed out. Do nothing. } catch (err) { // Only thrown for local problems: no parent frame, bad input, timeout. showError(err instanceof AppPaymentsError ? err.code : String(err)); } } ``` `requestPurchase` records *that* the user owns an item. Your app decides what owning it *does*. *** ## Things to know If the user already owns the item, Elata skips payment and returns `{ status: "success", txHash: "" }`. Code that checks `if (result.txHash)` will mishandle this. `CatalogItem.priceUsdc` is in USDC **base units** (6 decimals): `"50000"` is \$0.05. `RequestPurchaseInput.priceUsdc` is a free-form display hint Elata ignores. Elata always charges the catalog price. Each item is bought once. Consumables (spend-per-use balances) and recurring billing aren't supported through items. For recurring revenue use a [subscription plan](/apps/platform/subscriptions). `requestPurchase` waits up to 5 minutes for the user to finish checkout. `getCatalog`, `getOwnedItems`, and `hasItem` default to 10 seconds. Outside Elata, use a mock host for local tests. Treat an ownership-query failure as unknown ownership: offer a retry or sign-in action, and never grant a paid benefit merely because the query timed out. *** ## Errors Calls reject only with `AppPaymentsError`, which has a `code`: | Code | Meaning | | - | - | | `invalid_input` | `contentId` isn't a non-negative integer, or an option has the wrong type | | `no_window` | Not running in a browser | | `no_parent` | Not running inside Elata's frame | | `no_crypto` | `crypto.randomUUID` isn't available | | `timeout` | No reply in time | | `not_authenticated` | The user isn't signed in (ownership queries) | | `fetch_failed` | Elata couldn't look up ownership | A normal `cancelled` or `error` purchase outcome resolves; it doesn't reject. *** ## Security model * Elata's purchase records are created after server-side payment verification. Your UI should use host ownership queries as its source of truth. * Your app **never sees** payment details, wallet keys, or session tokens. * Replaying a recording request with the same `requestId` is idempotent. This does not make sending a second on-chain payment safe. * The splitter deducts the configured platform fee and sends the creator's share to the payout wallet. See [Revenue and payouts](/apps/operate/revenue-and-payouts). *** ## Try it * [`examples/iap-demo`](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/examples/iap-demo): one self-contained HTML file with a built-in mock store, so you can run the full flow locally. * [Package README on npm](https://www.npmjs.com/package/@elata-biosciences/app-payments) ## Payment and UI states Handle success, cancellation, and errors separately. Prevent overlapping purchase attempts. A successful payment and a recorded entitlement are separate steps; unlock only after Elata reports success. If a payment settles but recording fails, keep the transaction details and direct the user to [billing help](/users/billing-and-purchases#payment-problems-and-history). Do not automatically send another payment. The checkout retries temporary indexing delays, but recovery from every recording failure is not guaranteed. Ownership queries can require authentication. On `not_authenticated`, ask the user to sign in; on a lookup failure, show a retry state. Do not silently treat unknown ownership as a confirmed non-owner. The app does not connect wallets itself. Elata checkout manages wallet connection and payment confirmation, including the embedded-wallet interface when used. Credits for metered features are not an alternative payment balance for these items. ## Test your integration * Reopen the app and restore already-owned benefits from host ownership. * Show **Use** or **Owned** for purchased items and **Buy** for available unowned items. * Handle a successful already-owned response with an empty transaction hash. * Handle signed-out, cancelled, insufficient-funds, unavailable-checkout, and lookup-error states. * Deactivate an item and verify that existing owners can still use its benefit. For subscription access, use [Subscriptions](/apps/platform/subscriptions), rather than simulating recurring billing with repeated item purchases. # Metrics and Scores Source: https://docs.elata.bio/apps/platform/metrics-and-scores Record events and scores, and contribute to the biometric Score, with @elata-biosciences/app-metrics. ## Overview `@elata-biosciences/app-metrics` gives your app a per-user store for **event records** and **scores**, plus `reportAffect` for contributing to the user's cross-app biometric Score. ```bash theme={null} npm install @elata-biosciences/app-metrics ``` ```ts theme={null} import { createMetricsClient } from '@elata-biosciences/app-metrics'; const metrics = createMetricsClient(); await metrics.ready(); await metrics.record({ type: 'level_complete', data: { level: 3, time: 42 } }); await metrics.saveScore({ value: 1200, meta: { level: 3 } }); const best = await metrics.loadScores({ order: 'value_desc', limit: 10 }); const recent = await metrics.query({ type: 'level_complete', limit: 20 }); ``` Data is scoped to the (user, app) pair and never crosses app boundaries. Elata keeps a local copy and, for signed-in users, syncs it to the user's account so it follows them across devices. These records also power your [analytics](/apps/operate/analytics-and-health). *** ## API | Method | What it does | | - | - | | `ready()` | Resolves once the connection to Elata is set up | | `record({ type, data })` | Store an event record | | `query({ type?, since?, until?, limit? })` | Read records back | | `clear()` | Delete all of this app's records for the user | | `saveScore({ value, meta? })` | Store a numeric score | | `loadScores({ order?, since?, until?, limit? })` | Read scores. `order` is `"value_desc"` or `"timestamp_desc"`. | | `reportAffect(report)` | Contribute a session result to the biometric Score (see below) | | `dispose()` | Tear down the client | ### Limits * Each record can be up to 64 KB. * Each (user, app) pair has a 5 MB storage quota. * Calls fail with `quota_exceeded`, `invalid_payload`, or `rate_limited` when you exceed limits. Errors are thrown as `MetricsClientError`, whose `code` is one of the host codes above or `handshake_timeout`, `disposed`, or `transport`. *** ## reportAffect `reportAffect` sends a **derived session result**, never raw signal, to Elata so it can contribute to the user's cross-app biometric Score. ```ts theme={null} const result = await metrics.reportAffect({ dimension: 'calm', // 'calm' | 'stress' | 'focus' baselineValue: 0.42, // 0..1 sessionValue: 0.61, // 0..1 delta: 0.19, // -1..1 meanHr: 68, // optional session-average heart rate signalQuality: 0.85, // 0..1 confidence: 0.8, // 0..1 source: 'rppg', durationSec: 300, }); // result: { accepted, calibrating, score } ``` | Result field | Meaning | | - | - | | `accepted` | `false` if the sample was dropped, for example for low quality | | `calibrating` | `true` until the user has enough samples for a stable Score | | `score` | The user's Score, 0 to 100, once calibrated; otherwise `null` | Two things must be true or the call fails with `scope_denied`: 1. **Your app has the `biometrics` scope.** Contact the Elata team to enable it for your app. 2. **The user has consented.** Ask with [`elata:consent:request`](/apps/platform/consent-and-insights). Elata also re-checks consent on the server for every sample. *** ## For hosts The package also ships a host entry, `@elata-biosciences/app-metrics/host` (`createMetricsHost`), used by Elata itself. App developers don't need it. # Notifications Source: https://docs.elata.bio/apps/platform/notifications Show reminders to users through Elata while your app is open. ## Why your app can't notify directly Your app runs in a sandboxed, cross-origin iframe. Browsers block the `Notification` API there: permission is stuck at `denied` and `requestPermission()` never prompts. There are two supported options: | Option | Works when | Best for | | - | - | - | | **Elata-brokered reminders** (this page) | Elata tab is open | "Session complete", "time for your break" | | **Background push** | A separately implemented service worker, push subscription, and delivery service | Background notifications; installing alone does not provide this | *** ## Request a reminder ```js theme={null} window.parent?.postMessage( { type: 'elata:notify:request', requestId: 'optional-id', body: 'Focus block complete. Nice work!', }, '*', ); ``` * `body` is plain text. Elata strips control characters, collapses it to one line, and cuts it to **140 characters**. * You can't set the title, icon, click URL, or actions. Elata sets them. Elata replies with `{ type: 'elata:notify:state', requestId?, status }`: | status | Meaning | Suggested UI | | - | - | - | | `shown` | A notification was shown | Show reminders as on | | `undecided` | The user hasn't opted in yet; Elata is showing an "Allow reminders?" prompt | A neutral, pending state | | `blocked` | The user opted out, or the OS denied permission | Stop asking; explain notification settings | | `rate_limited` | More than 3 requests in a minute | Back off | | `unsupported` | This browser has no Notification API | Hide the setting | *** ## What Elata guarantees * **Elata-authored title.** Always `Elata · `, taken from your Elata listing, so apps can't impersonate the OS, Elata, or each other. * **Per-app opt-in.** Off by default. The first request shows an in-frame prompt, and the choice is remembered per app. * **Rate limit.** At most 3 reminders per app per minute. * **Foreground only.** Reminders appear only while Elata tab is open. Clicking one focuses Elata. *** ## Detecting Elata Use the bridge only when running inside Elata, and keep your direct path for standalone use while its page is running. Home-screen installation alone does not let this code run when the app is closed: ```js theme={null} const inStore = window.parent !== window; if (inStore) { window.parent.postMessage({ type: 'elata:notify:request', body }, '*'); } else if (Notification.permission === 'granted') { new Notification('My App', { body }); } ``` # Subscriptions Source: https://docs.elata.bio/apps/platform/subscriptions Configure monthly app access, trials, and manual renewal through Elata. An active subscription plan gates your whole app. Elata checks access before opening the app, so you do not need subscription checkout code inside it. You can also sell [one-time items](/apps/platform/in-app-purchases); their ownership is separate. Paid checkout requires the deployment's payment configuration. See [What's live](/overview/status). App subscription checkout uses a connected wallet paying USDC on Base and renews manually; it is not an automatic recurring charge. ## Configure your plan Open **My Apps → your app → Edit → Commerce → Monthly subscription**. | Setting | Behavior | | - | - | | Monthly price | A positive amount in USDC; enter the human-readable amount in the form | | Free trial | A whole number from 0 to 365 days; 0 means no trial | | Active | An inactive plan does not gate the app or offer renewal | Each app has one plan. Save using the plan's own action. Owners and authorized team members can manage it. Price edits do not rewrite previously recorded paid periods; review the current price when making a new checkout. ## Who gets access | User | Access | | - | - | | App owner or authorized team member | Access without buying the app's plan | | User with valid paid-period coverage | Access during that coverage | | User with a valid trial | Access until the trial ends | | User without any of these | Subscription prompt while the plan is active | With no active plan, the app is free to enter. These rules concern app access; they do not grant paid in-app items. ## The customer's flow 1. The user opens the app's subscription prompt from its page or locked play screen. 2. If eligible, they start the offered trial without payment. Paid checkout can run directly in the subscription prompt when its wallet and payment configuration are ready, or the user can proceed to **Settings → Billing → Subscriptions**. 3. They choose available current-month or next-month coverage and review the cart. 4. Elata checks settlement and records access after payment. Multiple apps can be paid for in one cart. Periods use calendar months in UTC. Next-month coverage starts on the first; current-month catch-up access, when offered, uses the coverage and prorated price shown in the cart. A trial does not cause an automatic charge when it ends. The user must complete checkout to renew. An unpaid cart selection does not extend access. Leaving the next month unpaid lets paid access expire; removing a cart selection does not refund an already-paid period. The previous `/profile/subscriptions` route redirects to the current [Billing → Subscriptions](https://app.elata.bio/settings/billing/subscriptions) page. ## Earnings and subscribers Paid app periods retain their payment amounts. The **Commerce** report shows those subscription payments as gross revenue before the splitter fee. Your share settles directly to the creator's payout wallet. See [Revenue and payouts](/apps/operate/revenue-and-payouts). The plan's subscriber count is an access signal, not an exact recurring revenue metric. Trials can contribute to the current-period count. ## Test before enabling Use a separate user account to check the trial, paid-current-period, paid-next-period, lapsed, and unavailable-checkout states. Owners and team members bypass the gate, so testing only as yourself will miss it. Tell users what the subscription includes and show the relevant end date. Avoid describing it as autopay or promising a refund action the billing UI does not provide. ## Next * [Monetize your app](/apps/build/monetize) * [Billing and purchases](/users/billing-and-purchases) * [Revenue and payouts](/apps/operate/revenue-and-payouts) # Quickstart Source: https://docs.elata.bio/apps/quickstart Build a biosignal app and publish it to Elata. This guide takes you from nothing to a submitted app. You'll need Node.js 20 or newer and a webcam. **Known issue:** the repository's `create-elata-demo` 0.12.1 has a packaging blocker and fails to start without sibling workspace metadata. Verify a fixed release before using it. For now, follow [Add rPPG to an existing app](/sdk/tutorials/rppg-existing-app) to set up the SDK in your own web app instead. See [What's live](/overview/status). Create a camera heart-rate (rPPG) starter app: ```bash theme={null} npm create @elata-biosciences/elata-demo my-app -- --template rppg cd my-app npm install ``` Prefer EEG, headband PPG, or a game? See the [template list](/sdk/create-elata-demo). ```bash theme={null} npm run dev ``` Open the local URL, allow camera access, and you should see a live pulse signal. Edit the app in `src/`. The [rPPG guide](/sdk/guides/rppg-browser) explains the session API, and the [platform APIs](/apps/platform/host-bridge) add purchases, saved progress, and scores. Create `public/elata.json`: ```json theme={null} { "version": "1.0.0" } ``` See [elata.json](/apps/build/elata-json). ```bash theme={null} npm run build:zip ``` This writes `app.zip` with `index.html` at its root. Go to [app.elata.bio/build-app](https://app.elata.bio/build-app), register your app name, upload `app.zip`, try it in the play window, add an icon and header image, and submit for review. The full walkthrough is in [Publish your app](/apps/build/launch-your-app). *** ## Next A deeper SDK tutorial What every upload needs Talk to Elata from your app # What Are Elata Apps? Source: https://docs.elata.bio/apps/what-are-elata-apps Browser apps built on the Elata SDK and published to Elata. The repository's `create-elata-demo` 0.12.1 has a packaging blocker: missing `app-metrics` version metadata prevents startup. Check [scaffolder status](/sdk/create-elata-demo) before using the commands below; for now, follow [manual rPPG integration](/sdk/tutorials/rppg-existing-app). ## The Idea An Elata app is a web app that uses the Elata SDK to work with the body's signals: heart rate from a webcam, EEG from a consumer headband, or PPG from a headband's optical sensor. You build it with standard web tooling, upload it to the [Elata](https://app.elata.bio), and users play it right in the browser. You build the product. Elata handles hosting, sign-in, payments, per-user storage, and distribution. *** ## What You Get When You Publish | Capability | What it gives you | | - | - | | **Hosting** | Upload a static build (or point to your own HTTPS URL). Elata serves it inside a secure, sandboxed frame. | | **Distribution** | an Elata listing at `app.elata.bio/app/` with discovery, reviews, comments, votes, and sharing. | | **Accounts** | Users are already signed in to Elata. Your app never handles passwords, wallets, or session tokens. | | **In-app purchases** | Sell one-time items through Elata checkout with [`app-payments`](/apps/platform/in-app-purchases). | | **Subscriptions** | Offer a monthly plan with an optional free trial. See [Subscriptions](/apps/platform/subscriptions). | | **Per-user storage** | Save games, settings, and scores with [`app-state`](/apps/platform/app-state) and [`app-metrics`](/apps/platform/metrics-and-scores). Signed-in users' data follows them across devices. | | **Biometric Score** | With user consent, contribute derived session results to the user's cross-app Score. See [Consent and insights](/apps/platform/consent-and-insights). | | **Reminders** | Send foreground notifications through Elata with [Notifications](/apps/platform/notifications). | *** ## How It Works 1. **Build** a web app with the [Elata SDK](/sdk/overview). The fastest start is `npm create @elata-biosciences/elata-demo my-app`. 2. **Register** your app name in the [Build an App](https://app.elata.bio/build-app) wizard. 3. **Upload** your build as a `.zip` (or give us an HTTPS URL) and try it in Elata's play window. 4. **Publish**: add an icon, header image, and description, then submit for review. 5. **Go live** once the release passes the automated security scan and a moderator approves it. The full sequence is covered in [App Lifecycle](/apps/app-lifecycle). *** ## Who This Is For * **Builders** who want to ship a biosignal app without building hosting, accounts, payments, and storage * **Device makers** who want their sensor to reach an app ecosystem (see [Device Integration](/integrate)) * **Users** who want to discover and play biosignal apps *** ## Next Builder or user? From upload to live Start building # What You Can Build Source: https://docs.elata.bio/apps/what-you-can-build App categories, example ideas, and the biosignals you can build with. ## App Categories Every app on Elata is listed in one of five categories. | Category | What it covers | Example | | - | - | - | | **Games** | Casual or competitive games driven by the body's signals | A runner that speeds up as your heart rate calms down | | **Neurometrics** | Measuring and visualizing brain and body signals | A live EEG band-power dashboard for a Muse headband | | **Wellness** | Breathing, meditation, recovery, and stress tools | Guided breathwork with live rPPG heart-rate feedback | | **Tools** | Utilities for training, focus, and self-tracking | A focus timer that logs a calm/stress trend per session | | **Experiments** | Research-style apps, studies, and prototypes | A reaction-time study that records per-session results | *** ## Building With Biometrics Elata apps are built around the [Elata SDK](/sdk/overview). Elata checks every upload for real SDK usage, so your app must use at least one sensor path: * **Camera rPPG** (`@elata-biosciences/rppg-web`): heart rate and pulse signals from any webcam, no extra hardware * **EEG over Web Bluetooth** (`@elata-biosciences/eeg-web` + `eeg-web-ble`): brain signals from supported headbands such as Muse * **Headband PPG** (`@elata-biosciences/ppg-web`): heart rate and HRV from a headband's optical sensor On top of the sensor layer you can add recording and analysis with [`biosignal-session`](/sdk/biosignal-session/getting-started) and [`biosignal-analytics`](/sdk/biosignal-analytics/getting-started). All signal processing runs on the user's device. Raw camera frames and raw biosignals never need to leave the browser. *** ## What Elata Adds Your app also gets Elata's platform features: sign-in, in-app purchases, subscriptions, per-user storage, a cross-app biometric Score, and reminders. See [Platform APIs](/apps/platform/host-bridge). *** ## Next From upload to live Start building Open source apps built on the SDKs # About the Archive Source: https://docs.elata.bio/archive/overview Earlier Elata projects and retired features, kept for reference. The Archive holds earlier open-source projects from Elata. They're no longer under active development, but their code is public and these pages are kept for reference. For what's available today, see [What's live](/overview/status). *** ## Archived projects | Project | What it was | Code | | - | - | - | | [AgentForge](/open-source/agentforge/overview) | Agent-based simulation framework for testing EVM smart contracts | [agentforge](https://github.com/Elata-Biosciences/agentforge) | | [NewsReader](/open-source/newsreader/overview) | AI-assisted news scanner behind news.elata.bio | [elata-vsm-system-4](https://github.com/Elata-Biosciences/elata-vsm-system-4) | | [ZORP](/zorp-protocol/zorp-intro) | On-chain research platform for encrypted data contribution; reached testnet in 2025 | [zorp](https://github.com/Elata-Biosciences/zorp) | | [Elata EEG](/home/elata-eeg/elata-eeg-overview) | Open-source EEG hardware, up to 32 channels | [elata-eeg](https://github.com/Elata-Biosciences/elata-eeg) | *** ## Retired features App tokens, staking, and governance are retired from the current product for now and are intended to return later. The future design and timing are not committed. Old links redirect to current guidance rather than implying these features are usable today. | Retired | Today | | - | - | | Launching a token for each app, bonding curves, and trading app tokens | Apps are registered with a one-time fee; no token is launched | | Staking for apps, tournaments, and veELTA pages | Not part of the live product | | "Premium content" | [In-app purchases](/apps/platform/in-app-purchases) and [subscriptions](/apps/platform/subscriptions) | | The API Reference tab | [Host bridge](/apps/platform/host-bridge) | # Integrate your device with the Elata SDK Source: https://docs.elata.bio/integrate Integrate a headset or biosignal device into the Elata EEG BLE stack with a simple, reliable path. This guide is for device companies that want their hardware to work with the Elata SDK. The target is simple: your device should plug into the shared Elata transport boundary and emit stable frames that apps can trust. Review the shared frame and transport types before writing adapter code. Confirm Web Bluetooth, browser, and platform constraints first. ## What You Are Building A production-ready integration should do three things well: 1. connect reliably 2. stream frames in a stable, predictable format 3. recover cleanly when the device, browser, or radio link misbehaves In Elata terms, that means converging on `HeadbandTransport` and `HeadbandFrameV1` instead of exposing vendor-specific packet layouts directly. Apps should depend on the shared Elata transport contract, not on raw vendor packets. That keeps downstream integrations simple and makes device support easier to maintain. ## The Fast Path Decide whether your device belongs upstream in `eeg-web-ble`, in a separate package, or in an app-local adapter. Use [Integration Paths](./integration-paths) to make that decision early. Gather your GATT services, characteristics, packet format, sample rate, channel names, timestamp behavior, and known firmware quirks. See [Protocol Requirements](./protocol-requirements). Build the device layer that handles discovery, session setup, packet decoding, and frame emission. Start from [Adapter Implementation](./adapter-implementation). Test connect, start, stop, disconnect, malformed packets, reconnects, and frame correctness before tuning anything downstream. Use [Testing and Validation](./testing-and-validation). Package the integration with platform notes, tested firmware versions, and support expectations. See [Submission and Support](./submission-and-support). ## What You Need Before You Start | Requirement | Why it matters | | - | - | | Device protocol details | You need a complete byte-level picture before implementation starts. | | A supported browser path | Web Bluetooth requires Chrome or Edge in a secure context. | | Stable channel metadata | Apps need consistent channel order, names, and sample rates. | | A clear ownership model | Decide early whether this will live upstream, in a vendor package, or privately. | If you have not reviewed the current BLE flow yet, read [EEG BLE Getting Started](/sdk/eeg-web-ble/getting-started) first. ## Guide Map Choose where the integration should live and how it should be owned. The shared interfaces your device must satisfy. The protocol and metadata inventory to collect before coding. Build the device adapter and connect it to `BleTransport`. Make the integration reliable under normal and failure conditions. Hand off the integration with the docs and caveats partners need. ## Reliability First A device integration is not done when it streams once on a happy path. It is done when: | Done means | Notes | | - | - | | `connect()` is predictable | discovery and session setup succeed or fail clearly | | `start()` produces stable frames | row width, channel order, and sample rate stay correct | | `stop()` is clean | the session stops without leaving the device in a bad state | | `disconnect()` is clean | the browser session releases resources correctly | | failures are surfaced clearly | apps can react to disconnects, retries, and unsupported states | ## Related Docs * [Headband Transport](/sdk/eeg-web/headband-transport) * [EEG BLE Getting Started](/sdk/eeg-web-ble/getting-started) * [Muse Device](/sdk/eeg-web-ble/muse-device) * [Compatibility](/sdk/operations/compatibility) * [Troubleshooting](/sdk/operations/troubleshooting) ## Next Pick the right ownership and packaging model before coding. Confirm the frame and lifecycle expectations your adapter must satisfy. # Integration Paths Source: https://docs.elata.bio/integration-paths Choose the right packaging and ownership model for a device integration. Use this page before you write adapter code. The right path depends on whether the integration is broadly useful, whether it needs vendor SDKs or native bridges, and whether it will be maintained as a public SDK surface. ## Recommended Default If the device is likely to be useful to the wider Elata SDK audience, put it upstream in `@elata-biosciences/eeg-web-ble`. That keeps transport behavior centralized and avoids parallel adapters across multiple apps. ## Choose By Outcome | Path | Best when | Why | | - | - | - | | Upstream device module in `eeg-web-ble` | The device is broadly useful and works through browser BLE | Best default for public SDK support | | Separate package under `packages/` | You need a vendor SDK, a license gate, or a localhost/native bridge | Keeps heavier integrations isolated | | App-local adapter | You are proving a concept or shipping a private integration | Fastest path when no shared package is needed | ## Path 1: Upstream in `eeg-web-ble` Choose this when: * the device can work through Web Bluetooth in a browser * the integration is likely to be reused by more than one app * you want one shared implementation of transport behavior This path usually means: 1. add a device module under the BLE package 2. reuse `BleTransport` 3. add mocked tests 4. update consumer docs and device notes This is the best path for a public, durable integration that should feel like a first-class part of the SDK. ## Path 2: Separate Package Choose this when the device needs more than a clean browser BLE flow. Typical reasons: | Situation | Why a separate package helps | | - | - | | Licensed or heavy vendor SDK | Keeps extra dependencies out of the main BLE package | | Localhost helper or native bridge | Lets you ship a bridge-specific transport cleanly | | Independent release cadence | Lets the vendor or partner own versioning and support | The package should still expose a `HeadbandTransport`, or a thin wrapper around one, so app code stays consistent with the rest of the Elata stack. ## Path 3: App-Local Adapter Choose this when speed matters more than reuse. This is a good fit for: * a private customer deployment * a fast proof of concept * evaluation work before a public package exists Keep the contract the same even if the code stays private. That makes it easier to move upstream later. ## Decision Order If multiple apps would benefit, start by assuming an upstream path. If Web Bluetooth will not work, or if a native bridge is required, move to a separate package. Shared support usually belongs upstream. Vendor-owned release lines often fit better in a separate package. No matter where the code lives, the app-facing contract should still be `HeadbandTransport` and `HeadbandFrameV1`. ## Paths to Avoid Do not choose an app-local adapter just because it is easy in the first hour. That usually creates extra work later if the integration becomes public. Do not push a heavy, bridge-based, or licensed integration into the shared BLE package either. Keep the public package lean. ## Related Docs * [EEG BLE Getting Started](/sdk/eeg-web-ble/getting-started) * [Transport Contract](./transport-contract) * [Adapter Implementation](./adapter-implementation) * [Compatibility](/sdk/operations/compatibility) ## Next Review the shared interfaces your chosen path must still satisfy. Gather the protocol details you need before implementation begins. # Core Concepts Source: https://docs.elata.bio/open-source/agentforge/concepts Scenarios, agents, ticks, packs, and determinism in AgentForge ## Scenarios A scenario is the top-level configuration for a simulation run. It defines: * **`name`** — identifier for the scenario * **`seed`** — deterministic RNG seed (same seed = same results) * **`ticks`** — number of simulation steps * **`tickSeconds`** — simulated time per tick (e.g., 3600 = 1 hour) * **`pack`** — protocol adapter that sets up world state * **`agents`** — list of agent types, counts, and parameters * **`assertions`** — post-run validation checks * **`gossip`** — channel definitions, budgets, and trust settings ```typescript theme={null} import { defineScenario } from '@elata-biosciences/agentforge'; export default defineScenario({ name: 'my-scenario', seed: 42, ticks: 200, tickSeconds: 3600, pack: myPack, agents: [ { type: MyAgent, count: 10 }, ], assertions: [ { type: 'gt', metric: 'totalVolume', value: 0 }, ], }); ``` *** ## Ticks A tick is one simulation step. During each tick: 1. The engine calls `step(ctx)` on every agent in order 2. Each agent returns zero, one, or up to three actions 3. The pack executes each action against the protocol state 4. Metrics, gossip, and artifacts are recorded Tick ordering can be configured: **priority-based** (default), **random**, or **custom**. The ordering policy affects outcomes — see the mechanism experiments for analysis. *** ## Agents Agents are autonomous actors. Every agent extends `BaseAgent` and implements `step()`: ```typescript theme={null} export class MyAgent extends BaseAgent { async step(ctx: TickContext): Promise { // Observe state, decide, and return actions } } ``` ### Agent Context Each tick, agents receive a `TickContext` with: | Property | Description | | - | - | | `ctx.rng` | Deterministic random number generator | | `ctx.world` | Current protocol state | | `ctx.gossip` | Read and post gossip messages | | `ctx.capabilities` | Available contracts, tools, and action templates | | `ctx.tick` | Current tick number | | `ctx.tickSeconds` | Simulated seconds per tick | ### Agent State Agents can persist state across ticks: * **`this.remember(key, value)`** — store a value * **`this.recall(key)`** — retrieve a stored value * **`this.setCooldown(name, ticks)`** — rate-limit an action * **`this.isOnCooldown(name)`** — check cooldown status ### Multi-Action Support Agents can return an array of up to 3 actions per tick. The engine executes each action in order within the same tick: ```typescript theme={null} return [ { id: this.generateActionId('swap', ctx.tick), name: 'swap', params: { ... } }, { id: this.generateActionId('post', ctx.tick), name: 'PostMessage', params: { ... } }, ]; ``` ### Persona LLM Agents `PersonaLlmAgentBase` provides a reusable base for LLM-driven agents with: * Persona profile (id, style, goals, risk profile, tool preferences) * Structured prompt assembly with full capability manifest context * Two-stage OODA loop: plan then act, with fallback to single-shot parsing * Schema-validated action intents via Zod *** ## Packs Packs are protocol adapters. They bridge AgentForge to your smart contracts by implementing the `Pack` interface: * **`setup()`** — deploy contracts, initialize state * **`executeAction(action)`** — execute an agent's action against the protocol * **`getWorldState()`** — return current protocol state for agents to observe * **`getCapabilityManifest()`** — describe available contracts, functions, and action templates The built-in `ToyPack` provides a simple price-simulation environment for getting started. *** ## Gossip The gossip bus enables inter-agent communication: * **Channels** — global broadcast or scoped strategy channels with membership lists * **Free-form messages** — text payloads with optional `credibilityPrior` scores (0-1) * **Budgets** — configurable `maxPostsPerTick` and `maxReadsPerTick` per agent * **Cooldowns** — per-channel `postCooldownTicks` to rate-limit posting ```typescript theme={null} gossip: { channels: [ { id: 'global', members: 'all' }, { id: 'strategy', members: ['LlmAgent-0', 'LlmAgent-1'] }, ], budgets: { maxPostsPerTick: 3, maxReadsPerTick: 10 }, } ``` Strategy channels are useful for LLM-only coordination without flooding deterministic agents. *** ## Determinism Same seed + same scenario = identical results. All randomness flows through a seeded RNG. This enables: * **Reproducible CI** — assertions pass or fail consistently * **Replay** — re-run exploration traces deterministically against updated contracts * **Comparison** — diff two runs with `forge-sim compare` ```bash theme={null} forge-sim run --toy --seed 123 --out run1 --ci forge-sim run --toy --seed 123 --out run2 --ci forge-sim compare run1/toy-market-ci run2/toy-market-ci ``` *** ## Exploration / Replay Workflow 1. **Explore** — run with LLM agents in exploration mode to discover behaviors 2. **Capture** — a `replay_bundle.json` records the full decision trace 3. **Replay** — re-run the exact same trace deterministically against updated contracts ```bash theme={null} forge-sim run scenario.ts --mode exploration forge-sim run scenario.ts --mode replay --replay-bundle results/run/replay_bundle.json ``` This allows you to discover emergent attack vectors with LLMs, then regression-test them after contract changes. # AgentForge Overview Source: https://docs.elata.bio/open-source/agentforge/overview Adversarial, agent-based simulation of EVM mechanisms ## What is AgentForge? AgentForge is an agent-based simulation framework for Foundry and EVM smart contracts. It fills the gap between isolated unit tests and production by simulating how a protocol behaves when many autonomous agents — traders, arbitrageurs, liquidators, LLM-driven strategists — act simultaneously with competing strategies over time. **Package**: [`@elata-biosciences/agentforge`](https://www.npmjs.com/package/@elata-biosciences/agentforge) on npm | Layer | What it tests | Example | | - | - | - | | Unit tests | Individual functions | `test_transfer()` | | Fuzz tests | Random inputs | `testFuzz_transfer(uint256 amount)` | | **AgentForge** | **Multi-actor emergent behavior** | **Traders, arbitrageurs, liquidators competing** | | Mainnet | Real users | Production | *** ## Key Features * **Deterministic simulation** — same seed + same scenario = identical results * **Multi-action agents** — agents can execute up to 3 actions per tick (e.g., trade + gossip) * **LLM-driven agents** — persona-based agents with structured prompt assembly and OODA loop * **Gossip bus** — bounded inter-agent communication with channels, budgets, and trust primitives * **Exploration/Replay workflow** — discover behaviors with LLM agents, then replay deterministically * **Studio dashboard** — Bloomberg Terminal-inspired UI with charts, timeline, gossip analysis, and ML toolkit * **Protocol adapters (Packs)** — plug into any EVM protocol via the `Pack` interface * **CI-ready** — exit codes, stable artifact naming, and GitHub Actions integration *** ## Quick Start ```bash theme={null} # Install pnpm add @elata-biosciences/agentforge # Scaffold a simulation folder npx forge-sim init # Run the built-in toy scenario npx forge-sim run --toy # Launch the Studio dashboard npx forge-sim studio ``` Requirements: **Node.js 18+** and optionally [Foundry](https://book.getfoundry.sh/getting-started/installation) with Anvil for EVM simulations. *** ## Writing Your First Agent Define an agent that observes state and decides actions each tick: ```typescript theme={null} import { BaseAgent, type Action, type TickContext } from '@elata-biosciences/agentforge'; export class MyTrader extends BaseAgent { async step(ctx: TickContext): Promise { if (ctx.rng.chance(0.3)) { return { id: this.generateActionId('buy', ctx.tick), name: 'buy', params: { amount: ctx.rng.nextInt(1, 100), asset: 'TOKEN' }, }; } return null; } } ``` Define a scenario that wires agents together: ```typescript theme={null} import { defineScenario } from '@elata-biosciences/agentforge'; import { ToyPack, RandomTraderAgent, MomentumAgent } from '@elata-biosciences/agentforge/toy'; export default defineScenario({ name: 'market-stress', seed: 42, ticks: 100, tickSeconds: 3600, pack: new ToyPack({ assets: [{ name: 'TOKEN', initialPrice: 100, volatility: 0.05 }], initialCash: 10000, }), agents: [ { type: RandomTraderAgent, count: 10 }, { type: MomentumAgent, count: 5, params: { threshold: 0.02 } }, ], assertions: [ { type: 'gt', metric: 'totalVolume', value: 0 }, { type: 'gte', metric: 'successRate', value: 0.9 }, ], }); ``` Run it: ```bash theme={null} npx forge-sim run sim/scenarios/market-stress.ts --seed 42 ``` *** ## Run Artifacts Every run produces durable artifacts: ``` results/market-stress-ci/ ├── summary.json # Run metadata, final metrics, assertion results ├── metrics.csv # Time-series data for analysis ├── actions.ndjson # Complete action log ├── gossip.ndjson # Gossip posts/deliveries ├── config_resolved.json # Resolved configuration for reproducibility ├── replay_bundle.json # Exploration trace for replay mode └── report.md # Generated report ``` *** ## Demo Integrations * [Uniswap v4 Fork Demo](https://github.com/wkyleg/v4-core/tree/main/sim/agentforge-demo) — deterministic + LLM agents simulating v4 pool mechanisms * [Elata Protocol](https://github.com/Elata-Biosciences/elata-protocol) — app launchpad with bonding curves *** ## Links * [npm package](https://www.npmjs.com/package/@elata-biosciences/agentforge) * [GitHub repository](https://github.com/Elata-Biosciences/agentforge) * [Full CLI Reference](/open-source/agentforge/cli) * [Core Concepts](/open-source/agentforge/concepts) * [Studio Dashboard](/open-source/agentforge/studio) # Apps on Elata Source: https://docs.elata.bio/overview/apps-on-elata What kinds of apps live on Elata, how they're reviewed, and how they run safely in your browser. ## What an Elata app is An Elata app is a web app that responds to signals from your body: your pulse from a webcam, brain activity from an EEG headband, or heart rate from a headband's optical sensor. You open it at [app.elata.bio](https://app.elata.bio) and play it in the browser. There's nothing to install. *** ## Kinds of apps | Category | Examples | | - | - | | **Games** | Games that react to your heart rate, focus, or calm | | **Neurometrics** | Live EEG and heart-rate dashboards | | **Wellness** | Breathwork, meditation, and recovery | | **Tools** | Focus timers and self-tracking | | **Experiments** | Studies, prototypes, and research apps | *** ## How apps get onto Elata Anyone can build an app with the open-source [Elata SDK](/overview/elata-sdk). To publish it, a builder registers the app on Elata, uploads the build, and submits it for review. Every release goes through: 1. **Automatic checks** when it's uploaded: the right file layout, size limits, and use of the Elata SDK. 2. **An automated security scan** of the code. 3. **A human moderator review** against the [submission policy](/apps/build/review-and-policy). Only approved releases are public. Builders can share a draft with a few playtesters before that. *** ## How apps run safely Each app runs in an **isolated sandbox** on its own web origin. That means: * Elata's bridge does not disclose your account email, wallet keys, or session tokens to the embedded app. Public activity and payment records can still be identifying. * It can ask for your camera or Bluetooth, and your browser asks you first. * When it needs something from Elata, like a purchase or saved progress, it sends a request and Elata handles it on your behalf. * Payment details never pass through the app. *** ## What builders get Builders don't have to build accounts, payments, hosting, or storage. Elata provides: | Capability | Status | | - | - | | Hosting, listing, reviews, and discovery | Live | | Saved progress and scores per user | Live | | Reminders through Elata | Live | | Contributing to the user's opt-in biometric Score | Live | | One-time in-app purchases | Implemented on the IAP overhaul branch; deployment and payment configuration must be verified | | Monthly subscriptions | Experimental | See [What's live](/overview/status) for details, and the [Build Apps](/apps/what-are-elata-apps) tab to get started. # Biosignals Explained Source: https://docs.elata.bio/overview/biosignals A plain-language guide to camera heart rate, EEG headbands, and headband PPG: what each measures and what it can't tell you. A **biosignal** is a measurable signal from the body, like a heartbeat or the brain's electrical activity. Elata apps use three kinds, all from devices people already own or can buy. *** ## Camera heart rate (rPPG) **What it is.** Each heartbeat pushes blood into the skin, which changes its color very slightly. A webcam can pick up that change, too small to see with your eyes. This is called remote photoplethysmography, or rPPG. **What it can estimate.** Pulse rate, and how steady the signal is. **What affects it.** Movement, dim or flickering light, strong shadows, video compression, and low frame rates all reduce accuracy. Accuracy can also vary with skin tone and camera quality. More detail: [Camera heart rate (rPPG)](/overview/science/rppg). *** ## EEG headbands **What it is.** Electroencephalography (EEG) measures tiny electrical signals from the brain through sensors on the scalp. Consumer headbands like Muse use a few dry sensors on the forehead and behind the ears. **What it can estimate.** How strong different rhythms are, such as alpha waves, which tend to rise when you're relaxed with your eyes closed. Apps turn these into measures like "calm" or "focus." **What affects it.** Blinking, jaw clenching, movement, and poor sensor contact create signals much larger than brain activity. A few sensors can't locate activity precisely, and "calm" or "focus" scores are estimates, not direct readouts of a mental state. More detail: [EEG headbands](/overview/science/eeg). *** ## Headband PPG and heart-rate variability **What it is.** Some headbands, like Muse S, have an optical sensor that shines light into the skin and measures the reflection, the same principle as a smartwatch heart-rate sensor (photoplethysmography, or PPG). **What it can estimate.** Heart rate and **heart-rate variability (HRV)**: the small variation in time between beats, which is often linked to recovery and stress. **What affects it.** Movement, a loose fit, and short recordings. HRV in particular needs clean data over a minute or more to be meaningful. More detail: [PPG and HRV](/overview/science/ppg-and-hrv). *** ## What these signals are not Elata apps are not medical devices. They don't diagnose, treat, or monitor any medical condition. If an app shows a reading that worries you, talk to a medical professional rather than relying on the app. These measurements are useful for games, feedback, and personal exploration. They are estimates that depend on conditions, and the same person can get different readings on different days. *** ## Where processing happens All three kinds of signal are processed on your device by the [Elata SDK](/overview/elata-sdk). Raw camera frames and raw biosignals don't need to leave your device. See [Privacy and consent](/overview/privacy-and-consent). # Community Source: https://docs.elata.bio/overview/community Where to find the Elata community, how to give feedback, and how to contribute. ## Join the conversation Chat with builders, users, and the Elata team Announcements and updates The Elata channel Research and industry news, curated for the community *** ## Give feedback * **About an app:** leave a review or a comment on its page on Elata, or use the app's own feedback button if it has one. * **About Elata itself:** use the feedback button on [app.elata.bio](https://app.elata.bio), or tell us on Discord. * **Report a problem with an app:** see [Help](/users/help). * **Email:** [info@elata.bio](mailto:info@elata.bio) *** ## Contribute The Elata SDK is open source. You can: * Report bugs and request features in [GitHub issues](https://github.com/Elata-Biosciences/elata-bio-sdk/issues) * Add support for a new headset by following [Device integration](/integrate) * Improve these docs: the [docs repository](https://github.com/Elata-Biosciences/docs) is public Other public projects are on [Elata's GitHub](https://github.com/Elata-Biosciences). *** ## Earn points for taking part Reviews, comments, sharing, and inviting friends all earn [Elata Points](/overview/points). *** ## Brand and media Logos and brand assets are on the [Media assets](/resources/media-assets) page. # How Elata Fits Together Source: https://docs.elata.bio/overview/ecosystem-map The ecosystem map: devices, the Elata SDK, apps, and Elata itself, and how data and value move between them. ## The big picture ```mermaid theme={null} graph LR D[Devices
webcam · EEG headband] --> S[Elata SDK
runs on the user's device] S --> A[Biosignal apps] A -->|sandboxed bridge| E[Elata
accounts · payments · storage · consent] E --> U[Users] U --> A E -.-> P[Points and the biometric Score
across all apps] ``` Elata has four layers. Each one has its own section in these docs. *** ## 1. Devices The sensors are things people already own: * **A webcam** can estimate pulse from tiny color changes in the skin (remote photoplethysmography, or rPPG). * **A consumer EEG headband**, such as Muse 2 or Muse S, measures electrical activity from a few points on the scalp, and some models also have an optical (PPG) heart-rate sensor. See [Devices you can use](/users/devices) and, for the science, [Biosignals explained](/overview/biosignals). *** ## 2. The Elata SDK The [Elata SDK](/overview/elata-sdk) is a set of open-source packages that connect to those devices and turn raw signals into usable measures such as heart rate, heart-rate variability, and EEG band powers. Processing runs in the browser, on the user's device, using WebAssembly. Builders use the SDK so they don't have to write signal processing from scratch. *** ## 3. Apps Builders combine the SDK with their own ideas: games you steer with focus or calm, breathwork guides that follow your heart rate, live dashboards, research tools. Apps are ordinary web apps. See [Apps on Elata](/overview/apps-on-elata). *** ## 4. Elata [Elata](https://app.elata.bio) is where those apps live. It: * **Reviews** every release (an automated security scan plus a human moderator) * **Hosts** each app in an isolated sandbox, so an app can't see who the user is or touch their account * **Provides** sign-in, purchases, subscriptions, and saved progress, so builders don't have to * **Asks for consent** in its own interface before a derived session result contributes to the platform biometric Score *** ## Across all apps Two things span every app on Elata: * **[Elata Points](/overview/points)** reward taking part: playing, reviewing, building, and inviting friends. * **The biometric Score** is an optional, cross-app measure built only from derived session results, and only if the user opts in. See [Privacy and consent](/overview/privacy-and-consent). *** ## How value moves | Who | Gives | Gets | | - | - | - | | Users | Attention, feedback, optional purchases | Apps that respond to their body, with privacy built in | | Builders | Apps | Distribution, built-in accounts and payments, and revenue from purchases and subscriptions | | Elata | The platform and the SDK | A one-time registration fee per app and a platform fee on sales and subscriptions | Details: [Monetization](/overview/monetization). *** ## Where data lives | Data | Where it stays | | - | - | | Raw camera frames and biosignals | On the user's device | | Saves, settings, and scores an app stores | The user's account, scoped to that one app | | Derived session results for the Score | Shared with Elata only with the user's consent | | What builders see | Aggregate usage analytics; Commerce reports can also show shortened buyer wallets and transaction links | # The Elata SDK Source: https://docs.elata.bio/overview/elata-sdk Open-source packages that turn webcams and consumer headbands into biosignal sensors, with processing on the user's device. ## What it does The Elata SDK is a set of open-source JavaScript packages for building apps on biosignals. It handles the hard parts: * **Connecting** to devices: a webcam, or a Bluetooth EEG headband such as Muse * **Processing** raw signals into usable measures: heart rate, heart-rate variability, EEG band powers, signal quality * **Running on the device**: the processing core is written in Rust and compiled to WebAssembly, so it runs in the browser and raw data doesn't have to go to a server The SDK is free to use, published on [npm](https://www.npmjs.com/org/elata-biosciences), and developed in the open on [GitHub](https://github.com/Elata-Biosciences/elata-bio-sdk). *** ## Package families | Family | Packages | Status | | - | - | - | | **Sensors** | `rppg-web` (webcam heart rate), `eeg-web` (EEG processing), `eeg-web-ble` (Bluetooth headbands), `ppg-web` (headband heart rate) | Live | | **Elata platform** | `app-payments` (purchases), `app-metrics` (records and scores) | Live | | | `app-state` (saved progress) | Experimental | | **Recording and analysis** | `biosignal-session` (local session recording), `biosignal-analytics` (features and scores), `rppg-models-web` (learned waveform models) | Experimental | | **Tooling** | `create-elata-demo` (starter apps) | Live, known issue | "Experimental" packages are available as source code but not yet on npm. See [What's live](/overview/status). *** ## Supported devices | Device | Signals | Browser | | - | - | - | | Any webcam | Pulse (rPPG) | Modern browsers | | Muse 2 and Muse S | 4-channel EEG, PPG | Chrome or Edge (Web Bluetooth) | | Muse S (Athena) | 8-channel EEG, optics, motion | Chrome or Edge (Web Bluetooth) | Other headsets can be added through the SDK's device contract. See [Device integration](/integrate). *** ## You don't have to publish on Elata The SDK works in any web app. Publishing on Elata adds distribution, accounts, and payments, but the sensor packages don't require it. Packages, installation, and device support A hands-on tutorial # ELTA Token Source: https://docs.elata.bio/overview/elta-token The facts about ELTA: what it is, where it's deployed, and how to verify the official contract. ## What ELTA is ELTA is the ERC-20 token for the Elata ecosystem. It has a fixed supply of 77,000,000 tokens, minted once when the contract was deployed. **No official liquidity pool exists yet.** Any pool or listing that appears before an official announcement from Elata is unauthorized. Always check that a contract address matches the one below exactly before interacting with it. *** ## Official addresses | Network | Address | Status | | - | - | - | | Base mainnet (canonical) | `0x6335495c88D1D78245BB50f321E4e450f865CfD0` ([BaseScan](https://basescan.org/address/0x6335495c88D1D78245BB50f321E4e450f865CfD0)) | Live | | Ethereum Sepolia (testnet) | `0x2AEb03A678A1e1E99a2AeEb4CeFCD0263A2D3587` ([Etherscan](https://sepolia.etherscan.io/address/0x2AEb03A678A1e1E99a2AeEb4CeFCD0263A2D3587)) | Testnet only | The canonical list of official addresses is maintained in the public [ELTA repository](https://github.com/Elata-Biosciences/ELTA/blob/main/OFFICIAL.md). *** ## Token facts | Property | Value | | - | - | | Name and symbol | Elata / ELTA | | Decimals | 18 | | Supply | 77,000,000, fixed. There is no way to mint more. | | Admin controls | None: no pause, upgrade, blacklist, or transfer fee | | Audit | [Distrust LLC](https://distrust.co), March 2026: 0 critical, high, medium, or low findings; 7 informational ([report](https://github.com/Elata-Biosciences/ELTA/tree/main/audits)) | *** ## ELTA and the Elata apps You don't need ELTA to use Elata. Browsing and playing apps, earning [Elata Points](/overview/points), and publishing apps don't require holding or spending ELTA. Elata Points are separate from ELTA: they're off-chain and can't be bought, sold, or transferred. Elata's wider on-chain protocol, including staking and governance, is not part of the live product. See [What's live](/overview/status). This page is factual information, not financial advice or an offer of any kind. *** ## Sources * [ELTA repository and README](https://github.com/Elata-Biosciences/ELTA) * [Official addresses](https://github.com/Elata-Biosciences/ELTA/blob/main/OFFICIAL.md) # Find Your Path Source: https://docs.elata.bio/overview/find-your-path Suggested reading for users, builders, researchers, partners and investors, and community members. ## I want to use Elata apps 1. [Get started](/users/get-started): open Elata, sign in, and try your first app 2. [Devices you can use](/users/devices): a webcam is enough, but you can use other devices too 3. [Your data](/users/your-data): what stays on your device and what you control 4. [Safety and limitations](/users/safety-and-limitations): what these measurements can and can't tell you *** ## I want to build an app 1. [Apps on Elata](/overview/apps-on-elata): what kinds of apps live here 2. [Quickstart](/apps/quickstart): scaffold, build, and submit your first app 3. [Elata SDK](/sdk/overview): the packages for camera, EEG, and PPG signals 4. [Monetization](/overview/monetization): how builders earn *** ## I'm a researcher 1. [Science overview](/overview/science/overview): Elata's scientific stance 2. [Camera heart rate](/overview/science/rppg) and [EEG headbands](/overview/science/eeg): how they work and their limits 3. [Metrics and scores](/overview/science/metrics-and-scores): how derived measures are built 4. [Data for research](/overview/science/data-for-research): what data exists and what doesn't *** ## I'm a partner 1. [What's live](/overview/status): current status of every capability 2. [How Elata fits together](/overview/ecosystem-map): the components and how value moves 3. [Mission and history](/overview/mission-and-history): where Elata came from 4. [Partners](/overview/partners): how to work with us *** ## I make a headset or sensor 1. [Device integration](/integrate): bring your device to the Elata SDK 2. [Transport contract](/transport-contract): the interface your device implements 3. [Submission and support](/submission-and-support): getting your integration listed *** ## I want to join the community 1. [Community](/overview/community): Discord, X, Farcaster, and GitHub 2. [Elata Points](/overview/points): earn points for taking part 3. [Contribute to the SDK](https://github.com/Elata-Biosciences/elata-bio-sdk): Elata SDK is open source # Glossary Source: https://docs.elata.bio/overview/glossary Plain-language definitions of the terms used across the Elata docs. **Alpha waves.** A rhythm in EEG between about 8 and 12 Hz, often stronger when a person is relaxed with their eyes closed. **App state.** Small pieces of data, such as saves and settings, that an app stores for a user through Elata. Scoped to that app and that user. **Artifact.** In EEG, a signal that doesn't come from the brain, such as a blink, jaw clench, or loose sensor. Artifacts are usually much larger than brain activity. **Band power.** How much of an EEG signal falls in a frequency band, such as alpha or beta. The basis of most "calm" and "focus" measures. **Biometric Score.** An optional, cross-app measure (0 to 100) built from derived session results, only with the user's consent. See [Privacy and consent](/overview/privacy-and-consent). **Biosignal.** A measurable signal from the body, such as a heartbeat or the brain's electrical activity. **Consent.** A user's explicit, revocable permission for an app to contribute derived results to their Score. Requested by Elata, not by the app. **Derived result.** A summary computed from raw signals, such as average heart rate over a session. Raw signals are never part of it. **EEG (electroencephalography).** Measuring the brain's electrical activity through sensors on the scalp. **Elata.** The marketplace at [app.elata.bio](https://app.elata.bio) where biosignal apps are published and played. **Elata Points.** Off-chain, non-transferable points earned for taking part on Elata. See [Points](/overview/points). **Elata SDK.** Open-source packages for building biosignal apps. See [The Elata SDK](/overview/elata-sdk). **ELTA.** The ERC-20 token for the Elata ecosystem, on Base. See [ELTA token](/overview/elta-token). **Federated learning.** Training a model across many devices without collecting their raw data in one place. Planned research direction for Elata. **Host bridge.** The set of messages an app uses to ask Elata for things like purchases and saved progress. See [Host bridge](/apps/platform/host-bridge). **HRV (heart-rate variability).** The variation in time between heartbeats. Often linked to recovery and stress, and sensitive to recording conditions. **[Muse](choosemuse.com).** A consumer EEG headband line (Muse 2, Muse S, and the newer Muse S Athena) supported by the Elata SDK. **PPG (photoplethysmography).** Measuring pulse by shining light into the skin and detecting changes in the reflected light, as smartwatches do. **Release.** One version of an app on Elata. Every release is scanned and reviewed before it goes public. **rPPG (remote photoplethysmography).** Estimating pulse from a camera by detecting tiny color changes in the skin. **Sandbox.** The isolated environment each app runs in, on its own web origin, so it can't see who the user is or access their account. **Web Bluetooth.** A browser feature that lets web pages connect to Bluetooth devices. Available in Chrome and Edge, not in Safari or iOS browsers. **WebAssembly (WASM).** A format that lets fast, compiled code run in the browser. The Elata SDK's signal processing runs as WebAssembly. # Mission and History Source: https://docs.elata.bio/overview/mission-and-history Why Elata exists, and the public milestones that got it here. ## Mission Elata's goal is to make biosignal technology open and accessible: a marketplace for privacy-respecting biometric apps that bring personal insight to anyone, using devices they already have. Three principles run through everything Elata builds: * **Open.** The [Elata SDK](/overview/elata-sdk) and many earlier projects are open source. * **Private by design.** Raw signals are processed on the user's device. Platform Score contributions are opt-in; app saves, event records, scores, and payments have separate storage paths. * **Accessible.** Many apps need nothing more than a webcam, and consumer headbands cost a fraction of clinical equipment. In an [October 2025 interview](https://finance.yahoo.com/news/exclusive-either-control-minds-hand-134608461.html), co-founder Andreas Melhede described the aim as democratizing brain-computer interface technology, "by the people, for the people." *** ## How Elata got here The milestones below are history. For what's available today, see [What's live](/overview/status). | When | Milestone | Today | | - | - | - | | 2025 | **ElataEEG**: open-source EEG hardware, upgraded from 8 to 32 channels with lower noise | [Archived](/home/elata-eeg/elata-eeg-overview) | | 2025 | **ZORP**: an on-chain research platform for encrypted data contribution and participant compensation, which reached testnet | [Archived](/zorp-protocol/zorp-intro) | | 2025 | **Brain Pong at Token2049 Singapore**: a tournament where players controlled a game with EEG headsets, covered by WIRED | Event | | March 2026 | **Elata SDK** published on npm: browser packages for webcam heart rate, EEG headbands, and headband PPG | [Live](/overview/elata-sdk) | | 2026 | **Elata** at app.elata.bio: a marketplace where builders publish biosignal apps and users play them in the browser | [Live](/overview/apps-on-elata) | | May 2026 | **ELTA token** deployed on Base after an external audit (March 2026); no official liquidity pool yet | [Live](/overview/elta-token) | Elata has also released other open-source tools along the way, including **AgentForge** (agent-based simulation for EVM smart contracts) and the **NewsReader** that powers news.elata.bio. They're described in the [Archive](/archive/overview). *** ## Press * [Yahoo Finance: interview with Elata's founder](https://finance.yahoo.com/news/exclusive-either-control-minds-hand-134608461.html) (October 2025) * [Cointelegraph: "Decentralized science will bring the brain onchain"](https://www.tradingview.com/news/cointelegraph:f89b78615094b:0-decentralized-science-will-bring-the-brain-onchain/), an opinion piece by co-founder Andreas Melhede * [WIRED](https://www.wired.com/story/expired-tired-wired-wearables/) More on [elata.bio](https://elata.bio) and the [Elata news site](https://news.elata.bio/). *** ## Sources * [Elata 2025 year in review](https://elata.bio/blog/2025-year-in-review-elata) * [elata.bio](https://elata.bio) * [ELTA repository](https://github.com/Elata-Biosciences/ELTA) # Commercialization Source: https://docs.elata.bio/overview/monetization What users pay for, how builders earn, and where payments go. Elata lets builders sell one-time items and monthly app access through the platform's checkout. Users manage their payments in **Settings → Billing**; builders manage their app's monetization in **Edit → Commerce**. Availability depends on the deployment and payment configuration. See [What's live](/overview/status). The app purchase and subscription flow described here settles in USDC on Base when enabled. A preview can use real funds; check the network shown in checkout. ## What users can pay for | Purchase | What it provides | Where to manage it | | - | - | - | | In-app item | A one-time unlock inside one app | Settings → Billing → Purchases | | App subscription | Access to one subscription-gated app for the paid period | Settings → Billing → Subscriptions | | Credits | Access to supported metered features | Settings → Billing → Wallet | App subscriptions renew manually. Selecting an app for next month does not pay for it: complete checkout to secure access. Credits and [Elata Points](/overview/points) are separate from the USDC used to buy items and app subscriptions. App subscription access does not grant ownership of every in-app item. See [Billing and purchases](/users/billing-and-purchases) for checkout, access, and billing instructions. ## How builders earn Builders create products and subscription plans in their app's **Commerce** section. One-time items use [`app-payments`](/apps/platform/in-app-purchases); whole-app subscriptions require no purchase code inside the app. For direct app sales, checkout routes USDC through the payment splitter. The splitter deducts the configured platform fee and sends the creator's share to the app's payout wallet. There is no separate Elata creator balance to withdraw later. The **Revenue** report shows gross sales before the split. The **Funds** card shows the payout wallet's current USDC balance on Base. These are different numbers: wallet transfers, fees, and other wallet activity affect the balance. Start with [Monetize your app](/apps/build/monetize), then follow [Revenue and payouts](/apps/operate/revenue-and-payouts) to access and move your funds. ## Fees | Cost | When it applies | | - | - | | Registration fee | Once per app, at registration; the wizard shows the amount | | Platform fee | Deducted by the splitter from direct app payments | | Network or payment-provider fees | As applicable to checkout or transfers | ## App tokens Buying app items or subscription access does not require ELTA or an app token. The earlier app-token launch model is [retired from the current product for now](/overview/status#retired-from-the-current-product). Protocol features are intended to return later, with no committed timing or final design. # Partners Source: https://docs.elata.bio/overview/partners What Elata is today, and how device makers, researchers, and app studios can work with us. ## Elata today Elata is two things that are live now: * **[Elata](/overview/apps-on-elata)**, a marketplace at app.elata.bio where builders publish biosignal apps and users play them in the browser * **The [Elata SDK](/overview/elata-sdk)**, open-source browser packages for webcam heart rate, EEG headbands, and headband PPG Some features are experimental and some are planned. The [What's live](/overview/status) page lists every capability with its status, so you can see exactly what exists today. *** ## Ways to work with Elata | You are | Start here | What it looks like | | - | - | - | | **A device maker** | [Device integration](/integrate) | Implement the SDK's transport contract so your headset works in every Elata app | | **A researcher** | [Science](/overview/science/overview) | Build studies as Elata apps, or use the open-source SDK in your own tools | | **An app studio or developer** | [Build apps](/apps/what-are-elata-apps) | Publish on Elata and use its built-in accounts and payments | | **An investor or ecosystem partner** | [How Elata fits together](/overview/ecosystem-map) | Understand the components, then get in touch | *** ## Get in touch Email [info@elata.bio](mailto:info@elata.bio) with a short note about who you are and what you have in mind. *** ## Resources * [Mission and history](/overview/mission-and-history), including press coverage * [Media assets](/resources/media-assets): logos and brand files * [ELTA token](/overview/elta-token): official facts and addresses * [Elata on GitHub](https://github.com/Elata-Biosciences) These docs describe what exists today. They are not an offer of securities or financial advice, and they don't make forward-looking commitments. # Elata Points Source: https://docs.elata.bio/overview/points Non-transferable points you earn for using, building, and growing Elata. ## What points are Elata Points reward real participation in Elata: playing apps, writing reviews, building, and bringing friends. Points are tied to your account. They can't be bought, sold, or transferred. See your balance, history, and the leaderboard at [app.elata.bio/points](https://app.elata.bio/points). *** ## How to earn | Activity | Examples | | - | - | | **Showing up** | Daily sign-in, and bonuses for 3-, 7-, and 30-day streaks | | **Playing** | Playing apps, your first app played, weekly quests | | **Community** | Writing reviews, commenting, voting, following, saving apps | | **Sharing** | Sharing apps, and clicks on your shared links | | **Referrals** | A friend you invited joins and is verified | | **Profile** | Completing your profile, verifying your email, adding social links | | **Building** | Publishing an app, and your app reaching 100 and 1,000 plays | | **Purchases** | Buying in-app items | Most activities have a daily cap, and some can only be earned once. Point values and caps are set by Elata and may change; the Points page shows the current values. Referral points are only granted for genuine, verified sign-ups. Activity that looks like farming may be reversed. *** ## Quests Quests are short goals with a points reward, such as playing apps on two different days in a week. Your progress is tracked automatically. Claim the reward from the Points page once it's complete. *** ## Leaderboard and seasons The leaderboard ranks users by points earned over the week or month. Points are also grouped into seasons; a season's ranking doesn't change your lifetime total. *** ## Spending points You can spend points on profile badges and titles, such as **Supporter** and **Pioneer**, from the Points page. *** ## FAQ No. Points can only be earned through activity on Elata. The activity may have hit its daily cap, or it may be a one-time reward you've already earned. Some rewards, like referrals, are granted after a check that can take up to a day. Yes. Publishing an app and reaching play milestones both earn points. # Privacy and Consent Source: https://docs.elata.bio/overview/privacy-and-consent What stays on your device, what apps can store, how the opt-in biometric Score works, and your rights over your data. ## On-device first The Elata SDK processes biosignals on your device. Raw camera frames, raw EEG, and raw PPG don't need to leave it, and Elata's [submission policy](/apps/build/review-and-policy) forbids apps from sending them anywhere. Elata's [privacy policy](https://elata.bio/privacy) puts it this way: raw physiological signals never leave the user's device through the SDK, and the processing pipeline is designed to produce derived metrics instead of transmitting raw signals. A derived value can still be personal data when associated with an account or transaction. *** ## What apps can see The hosted app runs in a sandbox, and Elata's bridge does not give it your account email, wallet keys, or session tokens. When an app needs something from the platform, Elata answers on your behalf. This isolation does not make every interaction anonymous: public profile activity, purchase records, transaction hashes, or information you choose to enter may identify you. *** ## What apps can store An app can save data for you through Elata: * settings, saves, and progress * event records and scores That data is scoped to that one app and your account. Apps can't read each other's data. Usage [analytics](/apps/operate/analytics-and-health) show aggregates and suppress small cohorts. Commerce reports are different: they can show shortened buyer wallet addresses and transaction links, which should not be described as anonymous. *** ## The biometric Score The Score is a single, cross-app measure (0 to 100) built from **derived** session results, such as how much calmer you got during a session compared with your own baseline. Raw signal is never part of it. * **Off by default.** Nothing is shared until you opt in. * **Asked by Elata, not the app.** The consent prompt appears in Elata's own interface. * **Enforced on the server.** Every result is checked against your current consent before it counts. * **Revocable.** You can turn sharing off at any time. * **Versioned.** If the consent terms change materially, you're asked again. You can see your Score and activity on your **Insights** page, and inside apps that ask for it. See [Score and insights](/users/score-and-insights). *** ## Your rights Under Elata's privacy policy you can ask to access, correct, delete, export, or restrict your personal data. Email [info@elata.bio](mailto:info@elata.bio); the policy commits to responding within 30 days. Each app on Elata is built by an independent developer. Read an app's own terms if it links to any. *** ## What's next | Capability | Status | | - | - | | On-device processing | Live | | Opt-in Score with revocable consent | Live | | Federated learning: improving models by training across devices without collecting raw data | Planned (research) | See [Research directions](/overview/science/research-directions). *** ## Sources * [Elata privacy policy](https://elata.bio/privacy) # Data for Research Source: https://docs.elata.bio/overview/science/data-for-research What biosignal data exists in the Elata ecosystem, what doesn't, and the tools available for running your own studies. ## What data exists Elata is built so that raw biosignals don't need to leave the user's device. That shapes what data exists: | Data | Exists? | Where | | - | - | - | | Raw camera frames | Not collected by Elata's server pipeline | Processed locally | | Raw EEG and PPG | Not collected by Elata's server pipeline; local experimental recordings are possible | The user's browser storage when a compatible recording host is enabled | | Derived session results for the biometric Score | Yes, with consent | Elata, for users who opt in | | App saves, records, and scores | Yes | The user's account, scoped to each app | | Aggregate app analytics | Yes | Visible to each app's builder, anonymized | Elata does not currently offer research datasets or data access programs. *** ## Running your own study Researchers can use the open-source tools directly: * **Build a study as an Elata app.** Use the SDK for capture and processing, and publish it on Elata to reach participants in their browser. Your app is responsible for its own consent and study protocol. Apps may not send raw biosignals off the device under Elata's [submission policy](/apps/build/review-and-policy). * **Use the SDK in your own tools.** The sensor packages work in any web app, outside Elata, under your own institution's data handling. *** ## Recording sessions (experimental) Experimental. The SDK supplies the recording contract. The appstore's `feat/long-local-analytics` branch implements a local recording host, session pages, and storage controls, but explicitly disables them in production. `biosignal-session` records multi-sensor sessions (EEG, PPG, rPPG metrics, and more) as typed streams in Apache Arrow files, designed for reliability: * every chunk carries a checksum and a stable identity, so it can be stored and retried safely * real gaps are recorded as explicit discontinuities, never interpolated * time is a single session clock in integer microseconds Recording is local: the package has no network client. The application that embeds it decides where data is stored. See [biosignal-session](/sdk/biosignal-session/getting-started). *** ## Analysis tools (experimental) `biosignal-analytics` provides reproducible features and transparent scores, with every value traceable to the algorithm and version that produced it. See [Metrics and scores](/overview/science/metrics-and-scores). # EEG Headbands Source: https://docs.elata.bio/overview/science/eeg What consumer EEG headbands measure, how the Elata SDK processes it, and what calm and focus scores can and can't claim. ## The principle Neurons communicate with tiny electrical currents. When large groups fire together, the combined activity can be picked up by sensors on the scalp. That recording is an **electroencephalogram (EEG)**. It's dominated by rhythms at different frequencies: | Band | Approx. range | Often associated with | | - | - | - | | Delta | 1–4 Hz | Deep sleep | | Theta | 4–8 Hz | Drowsiness, some memory tasks | | Alpha | 8–12 Hz | Relaxed wakefulness, especially with eyes closed | | Beta | 13–30 Hz | Active thinking, alertness | | Gamma | 30+ Hz | Fast processing; also heavily contaminated by muscle activity | These associations are tendencies across groups of people, not rules for any single moment. *** ## Consumer headbands The Elata SDK supports the Muse family over Web Bluetooth: | Device | EEG channels | Sensor positions | | - | - | - | | Muse 2, Muse S | 4 | Behind the ears (TP9, TP10) and on the forehead (AF7, AF8) | | Muse S (Athena) | 8 | Additional channels on the newer protocol | Research-grade systems use 32 to 256 wet electrodes in a cap. A headband uses a handful of dry sensors in fixed positions. That makes it comfortable and affordable, and it limits what it can measure. *** ## How the Elata SDK processes EEG `eeg-web` runs in the browser using WebAssembly: * **Filtering and spectra:** removes drift and line noise, and computes the frequency spectrum of each window * **Band powers:** absolute, relative, and log power per band and channel * **Quality flags:** flatline, clipping, extreme amplitude, and line noise, so bad windows can be excluded * **Simple models:** for example, an alpha-rhythm detector and a calmness estimate built on band powers *** ## What affects the signal | Factor | Effect | | - | - | | **Blinks and eye movement** | Large spikes on the forehead sensors | | **Jaw clenching, talking, frowning** | Muscle activity that overwhelms higher frequencies | | **Poor contact** | Hair, dry skin, or a loose fit cause noise or flat channels | | **Movement** | Cable and sensor motion add slow drifts and bursts | | **Few, fixed sensors** | Activity can't be located precisely, and some brain areas aren't covered | Studies comparing consumer headbands with research systems find that they can capture real brain signals. Event-related potentials have been recovered with Muse, and four Muse electrodes performed close to a 32-electrode dataset on one emotion-classification task. But signal quality varies by device and setup: one 2024 comparison found Muse S had the weakest alignment with a research-grade amplifier of the devices tested. *** ## What calm and focus scores can claim A "calm" or "focus" score is a **product interpretation** built from band powers and other inputs. It is not a direct readout of a mental state. * Scores are most meaningful **relative to the same person's baseline**, not compared between people. * They should come with **quality information** and be withheld when the signal is poor. * Some popular measures don't hold up. For example, the SDK's Focus score deliberately does **not** use the theta/beta ratio, because that ratio isn't a valid measure of attention. See [Metrics and scores](/overview/science/metrics-and-scores). *** ## Sources Peer-reviewed literature, retrieved via PubMed: * Krigolson OE, et al. Choosing MUSE: validation of a low-cost, portable EEG system for ERP research. *Frontiers in Neuroscience*, 2017. [doi:10.3389/fnins.2017.00109](https://doi.org/10.3389/fnins.2017.00109) * Mikhaylov D, et al. Comparison of EEG signal spectral characteristics obtained with consumer- and research-grade devices. *Sensors*, 2024. [doi:10.3390/s24248108](https://doi.org/10.3390/s24248108) * Garcia-Moreno FM, et al. EEG headbands vs caps: how many electrodes do I need to detect emotions? The case of the MUSE headband. *Computers in Biology and Medicine*, 2024. [doi:10.1016/j.compbiomed.2024.109463](https://doi.org/10.1016/j.compbiomed.2024.109463) SDK implementation: [`eeg-web`](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/eeg-web) and [`biosignal-analytics`](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/biosignal-analytics) (public SDK repository). # Metrics and Scores Source: https://docs.elata.bio/overview/science/metrics-and-scores How Elata turns signals into features and scores, why scores withhold themselves, and how the biometric Score is built. There are two different things in this area, with different statuses: | | What it is | Status | | - | - | - | | **SDK headline scores** | Open-source score formulas in `biosignal-analytics` that builders can run on their own data | Experimental (source only, not on npm) | | **The biometric Score on Elata** | One cross-app Score per user, built from derived results apps report with consent | Live | *** ## From signals to features Before any score, the SDK computes **features**: well-defined, reproducible numbers. * **EEG window features:** band powers (absolute, relative, log), spectral entropy, dominant frequency, alpha peak, Hjorth parameters, and quality flags * **Pulse features:** heart rate, RMSSD, SDNN, and signal quality * **Robust statistics:** medians and median absolute deviations instead of means, so one bad sample doesn't skew the result * **Personal baselines:** a rolling 30-day median and range for each person, with outlier rejection *** ## SDK headline scores Experimental. `biosignal-analytics` is available as source code and may change before its first npm release. | Score | Describes | | - | - | | **Measurement Quality** | The recording, not the person. Always available alongside any other score. | | **Activation** | How far physiological arousal moved from the person's baseline | | **Recovery** | How well arousal came back down after an activation | | **Focus** | Engagement during a task | | **Readiness** | Day-to-day state against the personal baseline | | **Resilience** | How consistently someone recovers over time | These are **product interpretations, never measurements**, and the SDK says so. Two design rules keep them honest: 1. **Every contributor is visible.** Each score exposes its inputs with their weights, standardized values, and quality. 2. **Scores withhold themselves.** Instead of guessing, a score returns no value plus a reason when it can't be computed responsibly: | Reason | Meaning | | - | - | | Inputs missing | The measurement wasn't made | | Insufficient quality | It was made too poorly to read | | Insufficient baseline | There's no usable personal baseline to compare with | | No task context | Focus: nothing was being attended to | | Insufficient history | Readiness needs 14 qualified days; Resilience needs 21 days plus 6 recovered activations | A missing input never silently becomes a neutral middle value. *** ## The biometric Score on Elata The Score is a single number from 0 to 100 that reflects a user's results across all the apps they use, **only if they opt in**. How it's built: * **Apps report derived session results, never raw signal.** A report says, for example, that the user's calm level went from 0.42 at baseline to 0.61 during a five-minute session, with its signal quality and confidence. * **Improvement relative to the user's own baseline matters**, not absolute values that differ between people. * **Low-quality sessions are dropped**, and the Score shows as "calibrating" until there are enough good sessions to be stable. * **Consent is checked on the server** for every report, so an app can't contribute without it. * **Only approved apps can contribute.** An app needs a permission granted by Elata before its reports count. See [Privacy and consent](/overview/privacy-and-consent) and, for builders, [Consent and insights](/apps/platform/consent-and-insights). *** ## Sources * [`biosignal-analytics` README](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/biosignal-analytics) (public SDK repository) * [Metrics and scores](/apps/platform/metrics-and-scores) (builder docs) # Science at Elata Source: https://docs.elata.bio/overview/science/overview Elata's scientific stance: what the measurements are for, how claims are graded, and where to read more. ## Our stance Elata builds tools for **wellness, games, learning, and research**. The signals Elata apps use (camera heart rate, consumer EEG, and headband PPG) are real physiological measurements, but they come from consumer devices in uncontrolled settings. They are estimates, not clinical readings. Three rules follow from that: 1. **No diagnosis.** Elata and Elata apps don't diagnose, treat, or monitor medical conditions. 2. **Show the uncertainty.** Measures come with signal-quality and confidence values, and scores withhold themselves when the data isn't good enough rather than guessing. 3. **Grade every claim.** A measured heart rate and a "focus" score are different kinds of claim, and the SDK labels them differently. *** ## How claims are graded The SDK's analytics package (`biosignal-analytics`, currently [experimental](/overview/status)) gives every metric a registered definition with two labels. **Measurement class:** what kind of number it is. | Class | Meaning | Example | | - | - | - | | Measured | Read more or less directly from the signal | Heart rate | | Derived (deterministic) | Computed from measured values by a fixed formula | HRV (RMSSD), band power | | Model-inferred | Estimated by a statistical or learned model | A learned waveform reconstruction | | Product composite | A product interpretation combining several inputs | Focus, Readiness | **Evidence tier:** how strongly the metric is supported. | Tier | Meaning | | - | - | | Beta default | Well supported; shown by default | | Advanced | Supported, but for informed users | | Experimental | Early; may change or be removed | | Rejected | Evaluated and deliberately not used | Each value also records the exact algorithm and version that produced it, so it can be traced and recomputed when an algorithm improves. Feature code is tested against reference implementations (NumPy/SciPy) so the numbers agree across the Rust, WebAssembly, and JavaScript versions. *** ## In this section How a webcam estimates pulse, and its limits What four dry sensors can and can't tell you Optical heart rate and heart-rate variability How derived measures and scores are built What data exists, and what doesn't Planned and archived research work *** ## Sources * [`biosignal-analytics` README and registry](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/biosignal-analytics) (public SDK repository) # PPG and Heart-Rate Variability Source: https://docs.elata.bio/overview/science/ppg-and-hrv Optical heart rate from a headband, heart-rate variability, and the conditions that make HRV meaningful. ## Contact PPG **Photoplethysmography (PPG)** shines light into the skin and measures how much comes back. As blood volume pulses with each heartbeat, the reflected light changes. It's the same principle as rPPG, but with a sensor touching the skin, which gives a much stronger signal. Smartwatches use it, and so do some headbands, such as Muse S. The Elata SDK's `ppg-web` package reads a headband's optical sensor over Web Bluetooth and estimates heart rate and heart-rate variability on the device. *** ## Heart-rate variability (HRV) Your heart doesn't beat like a metronome. The time between beats varies slightly from beat to beat, and that variation is **heart-rate variability**. Higher HRV at rest is often linked with recovery and relaxation, and lower HRV with stress or fatigue, though it also varies with age, fitness, breathing, and time of day. The SDK reports two common time-domain measures: | Measure | What it is | | - | - | | **RMSSD** | Root mean square of successive differences between beats. Reflects short-term, beat-to-beat variation. | | **SDNN** | Standard deviation of the beat-to-beat intervals over the recording. Reflects overall variation. | Strictly, variability measured from PPG is **pulse-rate variability**. It closely tracks ECG-based HRV at rest, but the two aren't always interchangeable. *** ## What makes HRV meaningful | Condition | Why | | - | - | | **At rest** | Agreement between wearable and ECG-based HRV is very good to excellent at rest, and declines as activity increases | | **Long enough** | A minute or more of clean data; a few seconds isn't enough | | **Clean beat detection** | One missed or extra beat distorts RMSSD badly, so noisy segments must be excluded | | **Compared to yourself** | HRV differs widely between people; trends against your own baseline are more informative than absolute numbers | The SDK withholds HRV values (returns no value) when it can't detect beats reliably, instead of reporting a misleading number. *** ## What it can't claim PPG-based heart rate is widely used and generally accurate at rest. It does not diagnose arrhythmias or other heart conditions: published reviews note that ECG confirmation remains necessary for diagnosis. *** ## Sources Peer-reviewed literature, retrieved via PubMed: * Georgiou K, et al. Can wearable devices accurately measure heart rate variability? A systematic review. *Folia Medica*, 2018. [doi:10.2478/folmed-2018-0012](https://doi.org/10.2478/folmed-2018-0012) * Hejjel L, Béres S. Comment on "Pulse rate variability in cardiovascular health: a review on its applications and relationship with heart rate variability". *Physiological Measurement*, 2021. [doi:10.1088/1361-6579/abd332](https://doi.org/10.1088/1361-6579/abd332) * De Wever M, et al. Photoplethysmography for heart rate and rhythm monitoring: current evidence and clinical applications. *JACC: Clinical Electrophysiology*, 2026. [doi:10.1016/j.jacep.2026.06.034](https://doi.org/10.1016/j.jacep.2026.06.034) SDK implementation: [`ppg-web`](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/ppg-web) (public SDK repository). # Research Directions Source: https://docs.elata.bio/overview/science/research-directions Planned research directions and earlier research projects, each clearly labelled. Nothing on this page is part of the live product. Each item is labelled so it's clear what is planned and what is history. For what you can use today, see [What's live](/overview/status). *** ## Federated learning **Status: Planned (research).** Federated learning trains a shared model across many devices without collecting their raw data in one place. Each device improves the model locally and only model updates are combined. It fits Elata's on-device design, because it could let signal-processing models improve without raw biosignals ever leaving users' devices. It is a research direction, not a live feature. There is no federated training in Elata or the Elata SDK today. *** ## Open EEG hardware: ElataEEG **Status: Archived.** ElataEEG is an open-source EEG hardware project. In 2025 it was upgraded from 8 to 32 channels with reduced noise. It's no longer under active development, and the current Elata SDK targets consumer headbands instead. See [Elata EEG](/home/elata-eeg/elata-eeg-overview) in the Archive and the public [elata-eeg repository](https://github.com/Elata-Biosciences/elata-eeg). *** ## On-chain research: ZORP **Status: Archived.** ZORP is an on-chain research platform for encrypted data contribution and automated participant compensation, which reached testnet in 2025. It's no longer under active development. See [ZORP](/zorp-protocol/zorp-intro) in the Archive and the public [zorp repository](https://github.com/Elata-Biosciences/zorp). *** ## Sources * [Elata 2025 year in review](https://elata.bio/blog/2025-year-in-review-elata) * [elata-eeg](https://github.com/Elata-Biosciences/elata-eeg) and [zorp](https://github.com/Elata-Biosciences/zorp) (public repositories) # Camera Heart Rate (rPPG) Source: https://docs.elata.bio/overview/science/rppg How a webcam estimates pulse, how the Elata SDK does it, and the conditions that limit accuracy. ## The principle With every heartbeat, the volume of blood in the skin rises and falls. Blood absorbs light, so the skin's color changes very slightly in time with the pulse. A camera can detect this change even though it's invisible to the eye. Measuring it without contact is called **remote photoplethysmography (rPPG)**. *** ## How the Elata SDK does it `rppg-web` runs this pipeline in the browser, on the user's device: 1. **Find the face.** A face-landmark model locates skin regions such as the forehead and cheeks. 2. **Average the color.** For each video frame, the SDK averages the red, green, and blue values in those regions. 3. **Extract the pulse signal.** Established methods combine the color channels to cancel out lighting and motion: the green-channel method, CHROM (chrominance-based), and POS (plane-orthogonal-to-skin). 4. **Estimate the rate.** The SDK estimates beats per minute two ways, from the signal's frequency spectrum and from its autocorrelation, and checks that they agree. 5. **Report quality.** Every estimate comes with a signal-quality value and a confidence value, so an app can tell a clean reading from a guess. Raw video frames never leave the device. *** ## What affects accuracy rPPG works well under good conditions and degrades under poor ones. Published reviews consistently identify the same challenges: | Factor | Why it matters | | - | - | | **Motion** | Head movement changes how light hits the skin, which can swamp the pulse signal | | **Lighting** | Dim, uneven, or flickering light (some LED and fluorescent lights) adds noise | | **Skin tone** | Darker skin absorbs more light, which lowers signal amplitude for optical methods | | **Camera and compression** | Low frame rates and heavy video compression remove the tiny color changes rPPG depends on | | **Distance and framing** | Fewer skin pixels mean a weaker signal | Tips for users are on [Devices](/users/devices). Builders can use the SDK's quality and confidence values to prompt users or hold back a reading. *** ## What rPPG can and can't claim * **Can:** estimate pulse rate at rest in reasonable conditions, and show how steady the signal is. * **With caution:** heart-rate variability from a camera needs very clean, longer recordings. * **Can't:** diagnose heart rhythm problems or replace a medical device. Even contact PPG, used in smartwatches, needs ECG confirmation for diagnosis. *** ## Sources Peer-reviewed literature, retrieved via PubMed: * Debnath U, Kim S. A comprehensive review of heart rate measurement using remote photoplethysmography and deep learning. *Biomedical Engineering Online*, 2025. [doi:10.1186/s12938-025-01405-5](https://doi.org/10.1186/s12938-025-01405-5) * Chen W, et al. Deep learning and remote photoplethysmography powered advancements in contactless physiological measurement. *Frontiers in Bioengineering and Biotechnology*, 2024. [doi:10.3389/fbioe.2024.1420100](https://doi.org/10.3389/fbioe.2024.1420100) * De Wever M, et al. Photoplethysmography for heart rate and rhythm monitoring: current evidence and clinical applications. *JACC: Clinical Electrophysiology*, 2026. [doi:10.1016/j.jacep.2026.06.034](https://doi.org/10.1016/j.jacep.2026.06.034) SDK implementation: [`rppg-web`](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/rppg-web) (public SDK repository). # What's Live Source: https://docs.elata.bio/overview/status The status of every Elata capability: live, experimental, planned, or retired. This page distinguishes current capabilities, experimental integrations, and future work. Other pages link here for availability. Source review: October 11, 2026. Billing and Commerce guidance reflects the `fix/iap-system-overhaul` branch, not proof that every flow is deployed at app.elata.bio. Confirm the deployed UI and payment configuration before accepting payments. | Label | Meaning | | - | - | | **Live** | Available now to everyone. | | **Experimental** | Usable, but may change or have limits. | | **Planned** | Direction we're working toward. Not available yet. | | **Retired** | Removed. Listed so old links and posts make sense. | *** ## Elata (app.elata.bio) | Capability | Status | Notes | | - | - | - | | Browse, play, save, review, and follow apps | Live | [Explore apps](/users/explore-apps) | | Publish web apps (Build an App wizard, review, sandboxed hosting) | Live | [Quickstart](/apps/quickstart) | | Sign in with social login or an existing wallet | Live | | | Elata Points, quests, referrals, leaderboard | Live | [Points](/overview/points) | | Biometric Score, consent, and Insights | Live | Opt-in. [Privacy and consent](/overview/privacy-and-consent) | | Reminders from apps (notifications) | Live | Delivered by Elata, never by the app directly | | In-app purchases (USDC) | Implemented; deployment unverified | The overhaul checkout uses the USDC splitter on Base; availability requires payment configuration. Check the deployed checkout's network. [In-app purchases](/apps/platform/in-app-purchases) | | App subscriptions | Experimental | Manual calendar-month payments through the Base splitter; configuration and deployment must be verified. [Billing and purchases](/users/billing-and-purchases) | *** ## Elata SDK Repository APIs and published packages can differ. Check the installed package version and release notes before using source-only APIs; publication labels below are not a guarantee that every current-source feature is in the npm release. | Capability | Status | Notes | | - | - | - | | `rppg-web`, `eeg-web`, `eeg-web-ble`, `ppg-web` | Live | On npm. [SDK overview](/sdk/overview) | | `app-metrics`, `app-payments` | Live | On npm | | `create-elata-demo` (starter apps) | Packaging blocker in repository version 0.12.1 | Missing `app-metrics` version metadata prevents the packaged CLI from starting. Verify a fixed release before using it. | | `app-state` | Experimental | Not on npm yet. Apps can use the [`elata:state:*` messages](/apps/platform/app-state) directly. | | `biosignal-session`, `biosignal-analytics`, `rppg-models-web` | Experimental | Source code only, not on npm yet. APIs may change. | | Muse 2, Muse S, Muse S (Athena) over Web Bluetooth | Live | Chrome or Edge. [Devices](/users/devices) | | Any webcam for rPPG | Live | | *** ## Data and research | Capability | Status | Notes | | - | - | - | | On-device processing of raw signals | Live | Raw signals do not need to leave the device | | Local session storage, session detail, and on-device analytics | Experimental; disabled in production | Implemented on `feat/long-local-analytics` behind a developer flag. Recordings stay in that browser. [Local sessions and insights](/users/local-sessions-and-insights) | | Federated learning (training models without collecting raw data) | Planned | Research direction. Not part of the live product. [Research directions](/overview/science/research-directions) | *** ## ELTA token | Capability | Status | Notes | | - | - | - | | ELTA token on Base | Live | Deployed, with no official liquidity pool yet. [ELTA token](/overview/elta-token) | Elata's wider on-chain protocol (staking, app tokens, governance) is not part of the live product. Its source is in the public [elata-protocol](https://github.com/Elata-Biosciences/elata-protocol) repository, which notes it is pending an external audit. *** ## Retired from the current product App tokens, staking, and governance are retired for now, with a planned return later. No return date or final design is committed. Earlier documentation is historical; it does not describe today's publishing or payment flow. Other archived projects are listed separately. | Capability | What replaced it | | - | - | | Launching a token for each app, bonding curves, and trading app tokens | Apps are registered on Elata with a one-time fee; no token is launched | | Staking for apps, tournaments, and veELTA pages in these docs | Not part of the live product | | "Premium content" | [In-app purchases](/apps/platform/in-app-purchases) and [subscriptions](/apps/platform/subscriptions) | | The API Reference tab | [Host bridge](/apps/platform/host-bridge) for builders | | ElataEEG hardware, ZORP, AgentForge, NewsReader | Archived projects. See the [Archive](/archive/overview). | # Welcome to Elata Source: https://docs.elata.bio/overview/welcome What Elata is, who it's for, and how to find your way around these docs. ## What Elata is Elata is a platform for building and using consumer-friendly biosensing, neuroscience, and cognitive applications that respect your privacy. * **Elata** ([app.elata.bio](https://app.elata.bio)) is where people find and play these apps in the browser, and where builders publish them. * **The [Elata SDK](/overview/elata-sdk)** is a set of open-source packages that turn a webcam or a consumer headband into a biosignal sensor, with processing that runs on the user's own device. * **Privacy starts on your device.** Raw signal processing runs locally. App saves, event records, scores, and payment records can be stored by Elata; derived results contribute to the cross-app biometric Score only with consent. You can revoke that consent and request deletion. [Your data](/users/your-data) explains the different storage and deletion paths. You *don't need special hardware* to begin: camera-based apps can use the webcam or phone camera you already have. Apps on Elata can turn an ordinary webcam or phone camera into a biosensor, using subtle changes in your face and behavior to measure or estimate signals like heart rate, heart rate variability, breathing rate, blink rate, affective state, and more. Available outputs and reliability depend on the app, sensor, package version, signal quality, and algorithm. Heart rate, HRV, respiration estimates, and face-derived affect are different signals; a physiological activation score is not a direct measurement of emotion. See [Biosignals explained](/overview/biosignals) and [SDK output guidance](/sdk/rppg-web/getting-started#output-and-quality-guidance). We also support applications that can read brain activity over the web. These applications rely on consumer-grade EEG devices, such as [Muse](https://choosemuse.com/). Elata apps are for wellness, games, learning, and research. They are not medical devices and don't diagnose or treat any condition. See [Safety and limitations](/users/safety-and-limitations). *** ## Building technology that’s open, transparent, and private Elata is built around open-source software, transparent experimentation, and the idea that biosensing technology should be understandable, inspectable, extensible, and privacy-focused to the people using it. Much of the technology behind Elata is developed publicly on GitHub, from the SDKs that power today’s apps to earlier experiments that helped shape what Elata has become. A couple notable examples: * [**Elata EEG**](https://github.com/Elata-Biosciences/elata-eeg) is our open-source effort to build a low-cost, right-to-repair EEG hardware device for neuroscience research and fun DIY Brain-Computer Interface (BCI) projects, such as the [brain-controlled gaming competition](https://x.com/DeSciWorld/status/1978762697022865576?s=20) we did in Singapore in 2025. * [**ZORP**](https://github.com/Elata-Biosciences/zorp) is an experiment we did to initiate permissionless research infrastructure for collecting data, coordinating scientific studies, and rewarding participants. Not every experiment becomes part of the current Elata product, but we keep this work public because useful ideas, code, and research shouldn’t disappear when a project changes direction. You can explore our current and past work on [GitHub](https://github.com/Elata-Biosciences). *** ## Who these docs are for Find apps, play them, and control your data Build a biosignal app and publish it on Elata How the measurements work and where they fall short Work together with the Elata community or team Bring your hardware to the Elata SDK Discord, X, Farcaster, and GitHub *** ## How to read these docs Every capability in these docs has a status, so you always know what you can use today: | Label | Meaning | | - | - | | **Live** | Available now to everyone. | | **Experimental** | Usable, but may change or have limits. For example, a package that is only available as source code. | | **Planned** | Direction we're working toward. Not available, and details may change. | | **Retired** | Existed before and has been removed. Kept here only so old links make sense. | Pages that aren't fully live carry a tag in the sidebar. The full list is on the [Status](/overview/status) page. *** ## Where to start The ecosystem map: devices, SDK, apps, and Elata Suggested reading for each audience # Protocol Requirements Source: https://docs.elata.bio/protocol-requirements Collect the protocol, BLE, and metadata details needed before coding a device adapter. Do this work before implementation starts. A clean protocol inventory prevents most integration delays and avoids the common failure mode where the adapter works only for one test session or one firmware version. ## Required Handoff Collect this information for every device and firmware line you plan to support. | Area | What to capture | | - | - | | Discovery | device name patterns, service UUIDs, filters | | GATT layout | characteristics, direction, notification/read/write behavior | | Packet format | framing, byte order, headers, counters, checksums | | EEG payload | channel names, channel count, sample rate, units, scaling | | Timing | device timestamps, local timestamps, sequence behavior | | Session control | any commands required to start or stop streaming | | Failure modes | disconnect behavior, low battery behavior, firmware quirks | ## BLE Inventory Template Use this as the minimum intake format: ```md theme={null} ## Device - Model: - Firmware: - Transport: Web Bluetooth / bridge / native helper ## Discovery - Device name prefix: - Primary service UUIDs: - Required optional services: ## Characteristics - UUID: - purpose: - read / write / notify: - notes: ## EEG payload - channel names: - channel count: - sample rate: - units and scaling: - timestamp source: ## Session control - how streaming starts: - how streaming stops: - reconnect notes: ``` ## Packet Questions You Should Answer Early | Question | Why it matters | | - | - | | How does a packet start and end? | The decoder needs a deterministic framing rule. | | Is there a packet counter or sequence field? | This helps detect drops, reordering, and corruption. | | Is there a checksum or CRC? | This helps reject bad packets safely. | | How are samples packed? | The decoder needs the exact byte layout and endianness. | | Can multiple time steps arrive in one packet? | This affects row batching and timestamp handling. | | What changes across firmware versions? | You need predictable version gating and test coverage. | ## Metadata Rules Your adapter should never guess the basics. | Metadata | Rule | | - | - | | Channel names | Use a documented, stable order | | Channel count | Match the emitted EEG rows exactly | | Sample rate | Use the true output rate for the active mode | | Units | Document whether values are raw, scaled, or converted | | Clock source | Clearly state `device` or `local` | Do not tune models or downstream app logic until this metadata is verified. Most reliability issues start here. ## Browser and Platform Questions This guide focuses on device integration, but platform constraints still matter. Confirm these before planning a browser BLE rollout: | Question | What to confirm | | - | - | | Does the device work over Web Bluetooth? | Chromium browsers only for this workflow | | Does it require a secure context? | `https://` or `localhost` | | Does it need a native helper? | If yes, use a separate package or bridge path | | Does Safari or iOS matter? | Browser Web Bluetooth is not available there | See [Compatibility](/sdk/operations/compatibility) for the current platform expectations. ## Definition of Ready You are ready to implement when you can answer all of the following without guessing: 1. which transport path you are taking 2. how the browser discovers the device 3. how the session starts and stops 4. how packet bytes become EEG rows 5. how timestamps and channel metadata should be emitted 6. what caveats apply by firmware, browser, or platform ## Related Docs * [Integration Paths](./integration-paths) * [Transport Contract](./transport-contract) * [Compatibility](/sdk/operations/compatibility) * [Troubleshooting](/sdk/operations/troubleshooting) ## Next Use the protocol inventory to implement a clean device layer. Turn protocol assumptions into repeatable tests. # FAQ Source: https://docs.elata.bio/resources/faq Answers to common questions about Elata, its apps, the Elata SDK, privacy, points, and the ELTA token. ## About Elata A marketplace for privacy-respecting biosignal apps at [app.elata.bio](https://app.elata.bio), plus the open-source Elata SDK for building them. See [Welcome](/overview/welcome). No. Elata apps are for wellness, games, learning, and research. They don't diagnose, treat, or monitor any medical condition. See [Safety and limitations](/users/safety-and-limitations). The [What's live](/overview/status) page lists every capability as live, experimental, planned, or retired. *** ## Using apps No. Camera apps work with any webcam. EEG and headband heart-rate apps need a supported headband such as Muse. See [Devices](/users/devices). Camera apps work in modern browsers. Bluetooth headbands need Chrome or Edge (Web Bluetooth); Safari and iOS browsers don't support it. See [Devices](/users/devices). No. You can sign in with a social login. Connecting an existing wallet is optional. Browsing and playing are free. Some apps sell optional items or offer a monthly subscription. See [Monetization](/overview/monetization). *** ## Privacy Raw signals are processed on your device, and apps aren't allowed to send them anywhere. See [Privacy and consent](/overview/privacy-and-consent). An optional, cross-app measure built from derived session results. It's only built if you opt in, and you can turn it off at any time. See [Score and insights](/users/score-and-insights). Email [info@elata.bio](mailto:info@elata.bio). See [Your data](/users/your-data). *** ## Building apps Build a web app with the Elata SDK, then use the Build an App wizard at [app.elata.bio/build-app](https://app.elata.bio/build-app). See the [Quickstart](/apps/quickstart). Yes. Uploads must use at least one Elata sensor package (rPPG, EEG, or PPG). See [Requirements](/apps/build/requirements). A one-time registration fee in USDC, shown in the wizard. Sell one-time items or charge a monthly subscription. See [Monetize your app](/apps/build/monetize). Every release is scanned automatically and then reviewed by a moderator. You're notified on Elata and by email when it's approved. *** ## Points and the token Off-chain points you earn for taking part: playing, reviewing, building, and inviting friends. They can't be bought, sold, or transferred. See [Elata Points](/overview/points). No. See [ELTA token](/overview/elta-token). No official liquidity pool exists yet. Any pool that appears before an official announcement is unauthorized. See [ELTA token](/overview/elta-token). # Media & Branding Assets Source: https://docs.elata.bio/resources/media-assets Elata logos, logotypes, and brand assets. [***A more extensive media asset collection/branding guide can be found on our Github and viewed here.***](https://github.com/Elata-Biosciences/branding-assets) ## Logotypes ### Elata Green (O.G.) Logotype Light Sv ### Elata White (Dark Mode) Logotype Dark Sv ## Logos ### Elata Green (O.G.) Logo Elata Green Logo Sv ### Elata White (Dark Mode) Logotype Elata White Logo Sv ### Elata Black Logo Elata Black Logo Sv # Getting Started Source: https://docs.elata.bio/sdk/biosignal-analytics/getting-started EEG features, HRV, robust statistics, and transparent headline scores with @elata-biosciences/biosignal-analytics. `@elata-biosciences/biosignal-analytics` is not published to npm yet. The API below reflects the source in the [SDK repository](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/biosignal-analytics) and may change before the first release. ## What it does `@elata-biosciences/biosignal-analytics` turns biosignal data into features and scores in the browser, with no server or network connection: * **EEG window features** (WASM): band powers, spectral entropy, dominant frequency, alpha peak, Hjorth parameters, and quality flags * **HRV and robust statistics**: time-domain HRV, medians and MADs, rolling personal baselines * **Headline scores**: Measurement Quality, Activation, Recovery, Focus, Readiness, and Resilience, each with its contributors exposed * **A metric registry**: every metric has a registered definition, unit, evidence tier, and algorithm version It pairs naturally with [`biosignal-session`](/sdk/biosignal-session/getting-started), but doesn't require it. ```bash theme={null} # Not on npm yet. This command works after the first release. npm install @elata-biosciences/biosignal-analytics ``` *** ## Analyze an EEG window ```ts theme={null} import { analyzeEeg, initAnalyticsWasm } from '@elata-biosciences/biosignal-analytics'; await initAnalyticsWasm(); const result = await analyzeEeg({ samples, // samples[sampleIndex][channelIndex] sampleRateHz: 256, channels: ['TP9', 'AF7', 'AF8', 'TP10'], }); ``` One call per window keeps the JavaScript/WASM boundary cheap. The result includes the configuration and algorithm versions that produced it. *** ## Headline scores Scores are interpretations, not measurements, and they say so: * **Measurement Quality** describes the recording, not the person, and is always available. * **Activation, Recovery, Focus, Readiness, and Resilience** each list every contributor with its weight, z-score, and quality. * A score **withholds itself** (returns `null` with a reason) rather than guessing when inputs are missing, quality is poor, or there's no personal baseline yet. | Withhold reason | Meaning | | - | - | | `inputs_missing` | The measurement wasn't made | | `insufficient_quality` | It was made too poorly to read | | `insufficient_baseline` | No usable personal baseline yet | | `no_activation_detected` | Recovery: nothing qualified as an activation | | `recovery_incomplete` | Recovery: the recording ended before recovery | | `no_task_context` | Focus: there was no task to attend to | | `insufficient_history` | Readiness and Resilience: not enough days of data yet | `insufficient_history` includes counts, so you can tell the user exactly how many more days are needed. Readiness needs 14 qualified days; Resilience needs 21 plus 6 recovered activation episodes. Focus is deliberately not based on the theta/beta ratio, which isn't a valid attention measure. *** ## Run analysis off the main thread ```ts theme={null} import { createAnalyticsWorkerClient, launchAnalyticsWorker, } from '@elata-biosciences/biosignal-analytics'; const client = createAnalyticsWorkerClient({ createWorker: launchAnalyticsWorker }); ``` *** ## Entry points | Import | Contents | | - | - | | `@elata-biosciences/biosignal-analytics` | Everything, re-exported | | `…/registry` | Metric definitions, evidence tiers, algorithm versions | | `…/insights` | Personal baselines and headline-score formulas | | `…/worker` | The analytics worker entry | | `…/testing` | Fixture loaders and synthetic signal helpers | Feature code is verified against Python (NumPy/SciPy) reference fixtures, so the Rust, WASM, and TypeScript implementations agree. # Getting Started Source: https://docs.elata.bio/sdk/biosignal-session/getting-started Record multi-sensor biosignal sessions locally with @elata-biosciences/biosignal-session. `@elata-biosciences/biosignal-session` is not published to npm yet. The API below reflects the source in the [SDK repository](https://github.com/Elata-Biosciences/elata-bio-sdk/tree/main/packages/biosignal-session) and may change before the first release. ## What it does `@elata-biosciences/biosignal-session` records a biosignal session (EEG, PPG, rPPG metrics, and more) as a set of typed streams, chunked into self-contained [Apache Arrow](https://arrow.apache.org/) files. Recording is **local-only**: the package has no network client. It is built for reliability: * every chunk carries a CRC32C checksum and an idempotent `(session, stream, sequence)` identity * the recorder retries, applies backpressure, and never drops data silently * real gaps are recorded as explicit discontinuities, never interpolated *** ## The model ``` Session ─┬─ Source (headset, camera, synthetic) ├─ Stream (eeg, ppg, optics, imu, rppg-metrics, ppg-metrics, …) │ └─ Chunk (Arrow IPC file + CRC32C, sequence 0, 1, 2, …) └─ Event (markers and annotations) ``` Time is session-relative integer **microseconds**, anchored once when the session starts. *** ## Roles A recording has two sides that talk over a `MessagePort`: | Side | Responsibility | Provided by this package | | - | - | - | | **Recorder** (your app) | Buffers samples, closes chunks, encodes Arrow, checksums, retries | Yes: `RecorderCore`, device adapters, a worker launcher | | **Host** (trusted embedder) | Stores chunks durably and acknowledges each one | A reference in-memory host for tests (`createMemoryHost`) | The appstore has an experimental local host on `feat/long-local-analytics`, disabled in production. Use your own host page or the in-memory host for development and tests, or follow [Local sessions and insights](/users/local-sessions-and-insights) for the appstore development flow. This is browser-local storage, not cloud hosting. *** ## Install ```bash theme={null} # Not on npm yet. This command works after the first release. npm install @elata-biosciences/biosignal-session ``` | Import | Contents | | - | - | | `@elata-biosciences/biosignal-session` | Contracts, protocol types, error codes, time helpers, CRC32C | | `@elata-biosciences/biosignal-session/browser` | `RecorderCore`, handshake, Arrow encode/decode, device adapters, worker launcher | | `@elata-biosciences/biosignal-session/testing` | Synthetic source, in-memory host, fake clock, recorder harness | *** ## Device adapters Wrap the SDK objects you already use as recording sources: * `createHeadbandSource(transport)`: a `HeadbandTransport` from `eeg-web` / `eeg-web-ble` * `createRppgSource(options)`: an `rppg-web` session * `createPpgSource(options)`: `ppg-web` metrics *** ## Try it without hardware The testing entry runs a full recording against an in-memory host with a seeded synthetic source. Hours of virtual signal run in seconds: ```ts theme={null} import { createRecorderHarness, createSyntheticSource, } from '@elata-biosciences/biosignal-session/testing'; const h = createRecorderHarness(); const source = createSyntheticSource({ seed: 1234 }); await h.start(); await h.startSource(source); source.pump(60_000); // 60 s of synthetic EEG, rPPG, and PPG await h.advance(60_000); await source.stop(); await h.finalize(); h.core.state(); // "complete" ``` *** ## Read chunks back Every chunk is a complete Arrow IPC file that carries its own schema and identity, so you can decode one on its own, in JavaScript or in Python with `pyarrow`: ```ts theme={null} import { decodeChunk, readFloat32Column } from '@elata-biosciences/biosignal-session/browser'; const { table, identity } = decodeChunk(bytes); const tp9 = readFloat32Column(table, 'TP9'); ``` *** ## Full guide The step-by-step recording flow (clock anchor, handshake, opening streams, pushing samples, finalizing) and the host's guarantees are in the [recording guide](https://github.com/Elata-Biosciences/elata-bio-sdk/blob/main/docs/guides/using-biosignal-sessions.md). AI tools can read `node_modules/@elata-biosciences/biosignal-session/llms.txt`. To analyze what you record, see [biosignal-analytics](/sdk/biosignal-analytics/getting-started). # Create Elata Demo Source: https://docs.elata.bio/sdk/create-elata-demo Scaffold a ready-to-run Elata starter app from a template. **Known issue:** the repository's `create-elata-demo` 0.12.1 has a packaging blocker and fails to start without sibling workspace metadata. Verify a fixed release before using it. For now, follow [Add rPPG to an existing app](/sdk/tutorials/rppg-existing-app) to set up the SDK in your own web app instead. See [What's live](/overview/status). ## What it is `@elata-biosciences/create-elata-demo` generates a working Vite + React app wired to the Elata SDK. It's the fastest way to a running app, and a known-good baseline before you integrate the SDKs into your own project. *** ## Templates | Template | Aliases | What you get | Hardware | | - | - | - | - | | `rppg-demo` | `rppg` | Camera pulse and rPPG starter. **The default.** | Webcam | | `eeg-demo` | `eeg` | Browser EEG starter for SDK wiring, with Muse Web Bluetooth support | Muse (optional) | | `eeg-ble` | `ble` | Bluetooth-first EEG starter with the browser pairing flow | Muse | | `ppg-demo` | `ppg`, `muse-ppg` | Headband PPG starter showing BPM, RMSSD, and live diagnostics | Muse | | `pulse-game` | `pulse`, `recovery` | rPPG recovery game that runs in a sandboxed iframe and saves scores with `app-metrics`, end to end | Webcam | Building for Elata? `pulse-game` shows the full Elata pattern: the app runs in an iframe and talks to a host over the [metrics bridge](/apps/platform/metrics-and-scores). *** ## Usage ```bash npm theme={null} # Interactive: pick a template, then a project name npm create @elata-biosciences/elata-demo my-app # Pick a template directly npm create @elata-biosciences/elata-demo my-app -- --template rppg npm create @elata-biosciences/elata-demo my-app -- --template eeg-ble npm create @elata-biosciences/elata-demo my-app -- --template ppg npm create @elata-biosciences/elata-demo my-app -- --template pulse-game # List templates npx @elata-biosciences/create-elata-demo --list-templates ``` ```bash pnpm theme={null} pnpm create @elata-biosciences/elata-demo my-app pnpm create @elata-biosciences/elata-demo my-app -- --template rppg pnpm dlx @elata-biosciences/create-elata-demo --list-templates ``` | Flag | Meaning | | - | - | | `--template ` or `--template=` | Choose a template or alias | | `--list-templates` | Print the available templates | Without `--template`, the CLI asks you to pick one when run interactively, and uses `rppg-demo` otherwise. If you leave out the project directory, it asks for a name. *** ## What you get Every generated app includes: * a Vite + React + TypeScript app * Elata packages pinned to a compatible set of versions * a template-specific `README.md` * `npm run dev`: start the dev server * `npm run build`: type-check and build to `dist/` * `npm run build:zip`: build and write `app.zip`, ready to [upload to Elata](/apps/build/launch-your-app) ```bash theme={null} cd my-app npm install npm run dev ``` *** ## Inside another pnpm workspace If you scaffold inside an existing `pnpm` workspace that doesn't include the new folder, install and run it outside the workspace: ```bash theme={null} pnpm --dir my-app --ignore-workspace install pnpm --dir my-app --ignore-workspace run dev ``` *** ## Scaffolder or example apps? | If you want... | Use | | - | - | | A clean starter with pinned dependencies | `create-elata-demo` | | Full, product-shaped apps with routing, game loops, and charts | [Example apps](/sdk/guides/example-apps) | *** ## Next Scaffold and run a starter app Package decision guide Upload your app # Getting Started Source: https://docs.elata.bio/sdk/eeg-web-ble/getting-started Connect to EEG headband devices over Web Bluetooth ## When To Use This Package Use `@elata-biosciences/eeg-web-ble` when your app needs: * browser-side discovery of Muse-compatible devices * live EEG streaming over Web Bluetooth * normalized headband frames for app logic or downstream analysis Install it alongside `@elata-biosciences/eeg-web`. *** ## Installation ```bash theme={null} npm install @elata-biosciences/eeg-web-ble @elata-biosciences/eeg-web ``` `eeg-web-ble` depends on `eeg-web` for shared frame types and the WASM module. *** ## Requirements * Browser with **Web Bluetooth** support (Chrome/Edge on desktop or Android) * Served from a **secure context** (`https://` or `localhost`) * Safari/iOS does **not** support Web Bluetooth. See [Muse device](/sdk/eeg-web-ble/muse-device) for alternatives Check support before showing a "Connect" button: ```typescript theme={null} import { checkWebBluetooth } from "@elata-biosciences/eeg-web-ble"; const support = checkWebBluetooth(); if (!support.supported) { showMessage(support.message); // explains why, including iOS-specific guidance } ``` *** ## Basic Usage `startStreaming()` is the recommended default. It wraps `connect()` and `start()` together and avoids the common mistake of connecting successfully but never beginning to stream. ```typescript theme={null} import { initEegWasm, AthenaWasmDecoder } from "@elata-biosciences/eeg-web"; import { BleTransport } from "@elata-biosciences/eeg-web-ble"; // Initialize WASM first await initEegWasm(); // Create transport const transport = new BleTransport({ deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), }, }); // Handle incoming EEG frames transport.onFrame = (frame) => { console.log(`EEG samples: ${frame.eeg.samples.length} rows`); console.log(`Channels: ${frame.eeg.channelNames.join(", ")}`); }; // Handle connection status changes transport.onStatus = (status) => { console.log(`Transport: ${status.state}`, status.reason || ""); }; // Connect and start streaming (triggers Bluetooth device picker) await transport.startStreaming(); // ... later await transport.stop(); ``` *** ## BleTransport Lifecycle | Method | What it does | | - | - | | `startStreaming()` | Recommended: connects and starts the stream in one call | | `connect()` | Opens Bluetooth device picker, pairs, and prepares session | | `start()` | Begins EEG data stream; `onFrame` callbacks fire | | `stop()` | Stops the data stream; connection stays open | | `disconnect()` | Releases the Bluetooth session | *** ## BleTransportOptions ```typescript theme={null} const transport = new BleTransport({ sourceName: "my-app-ble", // name tag in frame.source deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), // for Athena headbands logger: (msg) => console.debug(msg), onDisconnected: () => console.warn("Device disconnected"), }, }); ``` | Option | Type | Description | | - | - | - | | `sourceName` | `string` | Identifier included in `HeadbandFrameV1.source` | | `deviceOptions` | `MuseDeviceOptions` | Passed to underlying `MuseBleDevice` | | `device` | `BleDeviceLike` | Inject a custom device implementation | *** ## Athena Support Muse S headbands with Athena firmware require an Athena decoder factory. Include it up front to keep both classic and Athena flows covered: ```typescript theme={null} import { AthenaWasmDecoder } from "@elata-biosciences/eeg-web"; import { BleTransport } from "@elata-biosciences/eeg-web-ble"; const transport = new BleTransport({ deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), }, }); ``` Athena headbands provide 8 EEG channels, optics, accelerometer/gyroscope, and battery data in each frame. *** ## Device Info After connecting, you can query device metadata: ```typescript theme={null} await transport.connect(); const isAthena = transport.getIsAthena(); const boardInfo = transport.getBoardInfo(); const channelNames = transport.getEegNames(); ``` *** ## Platform Caveats * Safari and iOS do not provide usable Web Bluetooth support for this workflow. * For Safari and iOS, use a native BLE shell, companion bridge, or hybrid WebView strategy with `@elata-biosciences/eeg-web` frame contracts as the boundary. * Firmware variants may differ in command behavior. *** ## Next Step-by-step BLE streaming guide Protocol details, characteristics, and compatibility End-to-end streaming and processing guide # Muse Device Source: https://docs.elata.bio/sdk/eeg-web-ble/muse-device MuseBleDevice internals, protocol details, and compatibility ## MuseBleDevice `MuseBleDevice` is the low-level Web Bluetooth class that handles device pairing, GATT characteristic subscriptions, and packet decoding. `BleTransport` wraps it to provide the normalized `HeadbandTransport` interface. You typically do not use `MuseBleDevice` directly. Use `BleTransport` instead. *** ## Supported Protocols | Protocol | Headbands | EEG Channels | Extra Data | | - | - | - | - | | **Classic** | Muse 2, Muse S (classic) | 4 (TP9, AF7, AF8, TP10) | PPG (3 channels) | | **Athena** | Muse S (Athena firmware) | 8 (TP9, AF7, AF8, TP10, AUX1-4) | Optics, accgyro, battery | Protocol is auto-detected during connection based on device characteristics. *** ## Classic Protocol Classic Muse headbands expose 4 EEG channels at 256 Hz. Each channel has its own GATT characteristic: | Characteristic | Channel | | - | - | | `273e0003-...` | TP9 | | `273e0004-...` | AF7 | | `273e0005-...` | AF8 | | `273e0006-...` | TP10 | PPG is available on 3 additional characteristics (`PPG1`, `PPG2`, `PPG3`). *** ## Athena Protocol Athena headbands use a different packet format with two main characteristics: | Characteristic | Data | | - | - | | `273e0013-...` | EEG (8 channels, multiplexed) | | `273e0014-...` | Auxiliary (optics, accgyro, battery) | Athena requires a decoder factory. Without one, connection will fail: ```typescript theme={null} import { AthenaWasmDecoder } from "@elata-biosciences/eeg-web"; const transport = new BleTransport({ deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), }, }); ``` *** ## MuseDeviceOptions ```typescript theme={null} interface MuseDeviceOptions { athenaDecoderFactory?: AthenaDecoderFactory; sleepMs?: (ms: number) => Promise; logger?: (message: string) => void; onDisconnected?: () => void; } ``` *** ## MuseBoardInfo Returned by `getBoardInfo()` after connection: ```typescript theme={null} interface MuseBoardInfo { device_name: string; sample_rate_hz: number; // 256 channel_count: number; // 4 or 8 eeg_channel_names: string[]; board_id: number; protocol: "classic" | "athena"; optics_channel_count: number; description: string; } ``` *** ## Browser Compatibility | Browser | Platform | Status | | - | - | - | | Chrome 56+ | Windows, macOS, Linux, Android | Supported | | Edge 79+ | Windows, macOS | Supported | | Opera 43+ | Desktop | Supported | | Safari | macOS, iOS | Not supported | | Firefox | All | Not supported | Web Bluetooth requires HTTPS. It will not work on `http://` (except `localhost` for development). *** ## Safari/iOS Workarounds Three strategies for iOS support: 1. **Native app shell** (recommended): implement BLE in Swift with CoreBluetooth, bridge frames to web UI 2. **Companion bridge**: native app streams frames over WebSocket/WebRTC to the web app 3. **Hybrid WebView**: `WKWebView` with native message handlers for BLE In all cases, use the `HeadbandFrameV1` schema as the interface boundary so browser and native transports emit the same frame shape. *** ## Next Transport API and connection guide Frame schema and transport interface # Getting Started Source: https://docs.elata.bio/sdk/eeg-web/getting-started Install and initialize the @elata-biosciences/eeg-web WASM package ## When To Use This Package Use `@elata-biosciences/eeg-web` when your app needs: * EEG analysis APIs in the browser * shared EEG web contracts used by higher-level integrations * browser-side access to Elata WASM models and signal-processing helpers This package does not implement Bluetooth device connection. Add `@elata-biosciences/eeg-web-ble` if you also need live Muse-compatible browser transport. New to the SDK? Start by scaffolding a demo app with `create-elata-demo` before wiring packages manually. See the [First App tutorial](/sdk/tutorials/first-app). *** ## Installation ```bash pnpm theme={null} pnpm add @elata-biosciences/eeg-web ``` ```bash npm theme={null} npm install @elata-biosciences/eeg-web ``` Requirements: Node.js 20+ for server-side usage and local repo tooling; modern browser with WebAssembly support for in-browser usage. *** ## WASM Initialization Before using any signal processing or model APIs, initialize the WASM module: ```typescript theme={null} import { initEegWasm } from "@elata-biosciences/eeg-web"; await initEegWasm(); ``` `initEegWasm` is idempotent. Calling it multiple times returns the same promise. For synchronous initialization (e.g., in a Web Worker): ```typescript theme={null} import { initEegWasmSync } from "@elata-biosciences/eeg-web"; initEegWasmSync(wasmModule); ``` *** ## Basic Usage Compute band powers from EEG data: ```typescript theme={null} import { initEegWasm, band_powers } from "@elata-biosciences/eeg-web"; await initEegWasm(); const sampleRate = 256; const eegSamples = new Float32Array(/* ... one channel of EEG samples ... */); const powers = band_powers(eegSamples, sampleRate); console.log("Alpha:", powers.alpha); console.log("Beta:", powers.beta); console.log("Theta:", powers.theta); console.log("Delta:", powers.delta); console.log("Gamma:", powers.gamma); ``` *** ## Package Structure `@elata-biosciences/eeg-web` is a thin TypeScript wrapper around WASM bindings generated by `wasm-bindgen`: * `initEegWasm` / `initEegWasmSync`: WASM initialization helpers * Headband frame types: normalized data schema for EEG transports * All WASM APIs: re-exported from the generated bindings ### Key Exports * `initEegWasm` * `initEegWasmSync` * `band_powers` * `WasmAlphaBumpDetector` * `WasmAlphaPeakModel` * `WasmCalmnessModel` * `AthenaWasmDecoder` * `createRppgPipeline` * Frame helpers: `getEegChannelSamples`, `getEegInterleavedSamples`, `getEegSignalBlock` * Preprocessing: `createEegPreprocessor`, `processHeadbandFrame` * Errors: `ElataError`, `isElataError`, `asElataError` Generated `wasm-bindgen` exports are re-exported for compatibility and SDK debugging. Avoid instantiating generated wrappers directly unless you are intentionally debugging the SDK itself. *** ## Next Not sure if this is the right package? See the decision guide. Step-by-step integration tutorial Band powers, FFT, spectrum analysis Alpha bump detection, calmness scoring Frame schema and transport interface # Headband Transport Source: https://docs.elata.bio/sdk/eeg-web/headband-transport Normalized frame schema and transport interface for EEG headband devices ## HeadbandFrameV1 The canonical data frame emitted by all headband transports. Every implementation (BLE, native bridge, synthetic) produces this same shape: ```typescript theme={null} interface HeadbandFrameV1 { schemaVersion: "v1"; source: string; // e.g., "muse-ble" sequenceId: number; emittedAtMs: number; eeg: HeadbandSignalBlock; // always present ppgRaw?: HeadbandSignalBlock; // PPG (if available) optics?: HeadbandSignalBlock; // Athena optics accgyro?: HeadbandSignalBlock; // Athena accelerometer/gyroscope battery?: HeadbandBatteryBlock; // battery level } ``` *** ## HeadbandSignalBlock A block of time-series samples for one or more channels: ```typescript theme={null} interface HeadbandSignalBlock { sampleRateHz: number; channelNames: string[]; // e.g., ["TP9", "AF7", "AF8", "TP10"] channelCount: number; samples: number[][]; // rows of [ch0, ch1, ch2, ch3] timestampsMs?: number[]; clockSource?: "device" | "local"; } ``` *** ## HeadbandTransportState Transport lifecycle states. `HeadbandTransportState` is a string enum: `HeadbandTransportState.Streaming === "streaming"`. | State | Description | | - | - | | `Idle` | Transport created, not connected | | `Connecting` | Connection in progress | | `Connected` | Connected, not streaming | | `Streaming` | Actively receiving frames | | `Degraded` | Connected but experiencing issues | | `Reconnecting` | Attempting to reconnect | | `Disconnected` | Disconnected from device | | `Error` | Unrecoverable error | *** ## HeadbandTransport Interface All transports implement this interface: ```typescript theme={null} interface HeadbandTransport { onFrame?: (frame: HeadbandFrameV1) => void; onStatus?: (status: HeadbandTransportStatus) => void; connect(): Promise; disconnect(): Promise; start(): Promise; stop(): Promise; } ``` **Lifecycle:** ```mermaid theme={null} stateDiagram-v2 [*] --> Idle Idle --> Connecting: connect() Connecting --> Connected: success Connected --> Streaming: start() Streaming --> Connected: stop() Connected --> Disconnected: disconnect() Streaming --> Disconnected: disconnect() Connecting --> Error: failure Streaming --> Degraded: signal issues Degraded --> Streaming: recovery Disconnected --> Connecting: connect() ``` *** ## HeadbandTransportStatus Status updates emitted via `onStatus`: ```typescript theme={null} interface HeadbandTransportStatus { state: HeadbandTransportState; atMs: number; reason?: string; errorCode?: string; recoverable?: boolean; details?: Record; } ``` *** ## Usage Pattern ```typescript theme={null} const transport: HeadbandTransport = /* BleTransport or other */; transport.onFrame = (frame) => { const eegSamples = frame.eeg.samples; // samples[sampleIdx][channelIdx]: each row is one time step [tp9, af7, af8, tp10] }; transport.onStatus = (status) => { console.log(`State: ${status.state}`, status.reason); }; await transport.connect(); await transport.start(); // ... later await transport.stop(); await transport.disconnect(); ``` *** ## Helpers `@elata-biosciences/eeg-web` includes helpers for reshaping frames: | Helper | Returns | | - | - | | `getEegChannelSamples(frame, channelIndex)` | One channel as a `Float32Array`, ready for `band_powers` | | `getEegInterleavedSamples(frame)` | All channels interleaved `[s0_ch0, s0_ch1, …]`, for models such as `WasmCalmnessModel` | | `getEegSignalBlock(frame, source?)` | The raw or processed EEG block | *** ## Next Connect to headband devices End-to-end streaming guide # Analysis Models Source: https://docs.elata.bio/sdk/eeg-web/models Alpha bump detection, alpha peak tracking, and calmness scoring All three models share the same shape: * construct with `new Model(sampleRateHz, channelCount)` * call `process(interleaved)` with a `Float32Array` of **interleaved** samples (`[s0_ch0, s0_ch1, …, s1_ch0, …]`); `getEegInterleavedSamples(frame)` produces this from a headband frame * `process` returns a result object, or `undefined` until enough data has accumulated (`min_samples()` tells you how much) * call `reset()` to clear state Because `channelCount` is only known once the first frame arrives, construct models inside your first-frame handler. ## WasmAlphaBumpDetector Detects alpha bumps: transient changes in alpha band power that indicate state transitions, such as eyes closing. ```typescript theme={null} import { initEegWasm, WasmAlphaBumpDetector, getEegInterleavedSamples } from "@elata-biosciences/eeg-web"; await initEegWasm(); const detector = new WasmAlphaBumpDetector(256, 4); // sampleRateHz, channelCount const result = detector.process(getEegInterleavedSamples(frame)); if (result?.state_changed) { console.log(result.state, result.alpha_power, result.baseline); // state: "high" | "low" | "transitioning" | "unknown" } ``` Tune with `set_threshold(multiplier)` and `set_baseline_smoothing(alpha)`. *** ## WasmAlphaPeakModel Tracks the individual alpha peak frequency (typically 8 to 13 Hz), which varies from person to person. ```typescript theme={null} import { WasmAlphaPeakModel } from "@elata-biosciences/eeg-web"; const model = new WasmAlphaPeakModel(256, 4); const result = model.process(interleaved); if (result) { console.log(result.peak_frequency, result.smoothed_peak_frequency, result.snr); } ``` *** ## WasmCalmnessModel Computes a calmness score from the ratio of alpha to beta power. Higher alpha relative to beta generally indicates a more relaxed state. ```typescript theme={null} import { WasmCalmnessModel } from "@elata-biosciences/eeg-web"; const model = new WasmCalmnessModel(256, 4); const result = model.process(interleaved); if (result) { console.log(result.score); // 0.0 (alert) to 1.0 (very calm) console.log(result.smoothed_score); // less jittery console.log(result.alpha_beta_ratio); } ``` *** ## AthenaWasmDecoder Decodes raw Athena protocol packets from Muse S (Athena firmware) headbands. Used as a factory parameter for `BleTransport`: ```typescript theme={null} import { AthenaWasmDecoder } from "@elata-biosciences/eeg-web"; const decoder = new AthenaWasmDecoder(); decoder.reset(); decoder.set_use_device_timestamps(true); decoder.set_clock_kind("monotonic"); decoder.set_reorder_window_ms(50); const output = decoder.decode(rawBytes); ``` The Athena decoder is typically not used directly. It is passed as a factory to [`BleTransport`](/sdk/eeg-web-ble/getting-started): ```typescript theme={null} import { BleTransport } from "@elata-biosciences/eeg-web-ble"; import { AthenaWasmDecoder } from "@elata-biosciences/eeg-web"; const transport = new BleTransport({ deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), }, }); ``` *** ## Next Band powers, FFT, spectrum analysis Frame schema and transport interface # Signal Processing Source: https://docs.elata.bio/sdk/eeg-web/signal-processing Band power analysis, FFT, and spectrum computation ## Band Powers Compute standard EEG frequency band powers from a sample array: ```typescript theme={null} import { initEegWasm, band_powers } from "@elata-biosciences/eeg-web"; await initEegWasm(); const powers = band_powers(samples, sampleRateHz); // powers.delta 0.5-4 Hz // powers.theta 4-8 Hz // powers.alpha 8-13 Hz // powers.beta 13-30 Hz // powers.gamma 30-100 Hz ``` `band_powers` returns a `WasmBandPowers` object with named fields for each standard band. *** ## Individual Band Functions For targeted analysis, use individual band functions: ```typescript theme={null} import { alpha_power, beta_power, theta_power, delta_power, gamma_power, custom_band_power, } from "@elata-biosciences/eeg-web"; const alpha = alpha_power(samples, sampleRateHz); const beta = beta_power(samples, sampleRateHz); const theta = theta_power(samples, sampleRateHz); const delta = delta_power(samples, sampleRateHz); const gamma = gamma_power(samples, sampleRateHz); // Custom frequency range const mu = custom_band_power(samples, sampleRateHz, 8.0, 12.0); ``` *** ## Power Spectrum Compute the full power spectrum via FFT: ```typescript theme={null} import { compute_power_spectrum, get_fft_frequencies } from "@elata-biosciences/eeg-web"; const spectrum = compute_power_spectrum(samples); // samples: Float32Array const frequencies = get_fft_frequencies(samples.length, sampleRateHz); // spectrum[i] is the power at frequencies[i] Hz ``` *** ## Input Format All signal processing functions expect: | Parameter | Type | Description | | - | - | - | | `samples` | `Float32Array` | Single-channel EEG time series. Plain arrays are not accepted. | | `sampleRateHz` | `number` | Sampling rate in Hz (e.g., 256 for Muse) | For multi-channel data (e.g., from `HeadbandFrameV1.eeg.samples`), extract individual channels and process them separately: ```typescript theme={null} const frame: HeadbandFrameV1 = /* ... from transport ... */; const channelIndex = 0; // TP9 const channelSamples = frame.eeg.samples.map(row => row[channelIndex]); const powers = band_powers(new Float32Array(channelSamples), frame.eeg.sampleRateHz); ``` *** ## Next Alpha bump detection, calmness scoring Install and initialize eeg-web # Choosing the right package Source: https://docs.elata.bio/sdk/guides/choose-the-right-package Pick the right Elata SDK package for what you're building. ## Start here 1. **New app or just evaluating?** Use [`create-elata-demo`](/sdk/create-elata-demo). You'll have a working app in minutes. 2. **Existing app?** Add the package for the capability you need, from the table below. 3. **Publishing to Elata?** Add the [Elata packages](#elata-packages) for purchases, storage, and scores. *** ## By goal | I want to... | Use | Install with | | - | - | - | | Measure heart rate from a webcam | [`rppg-web`](/sdk/rppg-web/getting-started) | | | Process EEG in the browser (band powers, models) | [`eeg-web`](/sdk/eeg-web/getting-started) | | | Connect an EEG headband over Bluetooth | [`eeg-web-ble`](/sdk/eeg-web-ble/getting-started) | `eeg-web` | | Get heart rate and HRV from a headband's PPG sensor | [`ppg-web`](/sdk/ppg-web/getting-started) | `eeg-web`, `eeg-web-ble`, `rppg-web` | | Record a session from one or more sensors | [`biosignal-session`](/sdk/biosignal-session/getting-started) | the sensor packages you record from | | Compute features, HRV, and headline scores | [`biosignal-analytics`](/sdk/biosignal-analytics/getting-started) | | | Experiment with learned rPPG waveform reconstruction | `rppg-models-web` (experimental) | `rppg-web` | *** ## Elata packages These only work when your app runs inside the [Elata](/apps/build/overview): | I want to... | Use | | - | - | | Sell one-time items | [`app-payments`](/apps/platform/in-app-purchases) | | Save settings and progress per user | [`app-state`](/apps/platform/app-state) | | Record sessions and scores, or contribute to the biometric Score | [`app-metrics`](/apps/platform/metrics-and-scores) | *** ## Common mistakes These cause most first-run problems. * **Installing `eeg-web-ble` without `eeg-web`.** The Bluetooth transport depends on it. * **Expecting Web Bluetooth in Safari or on iOS.** Use Chrome or Edge on desktop or Android. See [Compatibility](/sdk/operations/compatibility). * **Serving over plain `http://`.** Camera and Bluetooth need `https://` or `localhost`. * **Assuming a scaffolded app is broken inside another pnpm workspace.** Install it with `pnpm --dir my-app --ignore-workspace install`. *** ## Next Scaffold your first app Full reference implementations Browser, device, and tooling support # EEG + BLE integration Source: https://docs.elata.bio/sdk/guides/eeg-ble-integration Connect a Muse-compatible headband in the browser, stream EEG frames, and compute band powers with the Elata SDK. Use this guide when you want the full browser EEG flow in one place. You will initialize the EEG WASM layer, connect over Web Bluetooth, stream frames, and turn those frames into app-ready metrics. ## What this guide covers This guide shows how to combine `@elata-biosciences/eeg-web` and `@elata-biosciences/eeg-web-ble` into a single browser flow. By the end, you will be able to: * initialize the EEG WASM runtime * connect to a Muse-compatible headband from a browser * receive normalized `HeadbandFrameV1` frames * compute real-time band powers from incoming EEG samples * stop cleanly and recover from disconnects ## Before you start You need: * `@elata-biosciences/eeg-web` * `@elata-biosciences/eeg-web-ble` * Chrome or Edge with Web Bluetooth support * `https://` or `localhost` * a Muse 2 or Muse S headband Safari and iOS do not support this browser BLE workflow. For those platforms, use a native BLE shell, a bridge app, or another transport layer and keep `@elata-biosciences/eeg-web` as your processing boundary. ## Choose the right starting point Scaffold an EEG BLE demo if you want the fastest path to a reference app. Learn the browser EEG processing package before adding device transport. Review the transport package, lifecycle methods, and platform constraints. Follow the step-by-step version if you want a tutorial before this end-to-end guide. ## Install the packages ```bash theme={null} pnpm add @elata-biosciences/eeg-web @elata-biosciences/eeg-web-ble ``` ```bash theme={null} npm install @elata-biosciences/eeg-web @elata-biosciences/eeg-web-ble ``` `@elata-biosciences/eeg-web-ble` depends on `@elata-biosciences/eeg-web` for shared frame types and the EEG WASM layer. ## Integration flow Initialize the EEG WASM module before you create a transport or call analysis helpers. ```ts theme={null} import { initEegWasm } from "@elata-biosciences/eeg-web"; await initEegWasm(); ``` Include an Athena decoder up front so both classic and Athena-compatible headbands work through the same setup. ```ts theme={null} import { AthenaWasmDecoder } from "@elata-biosciences/eeg-web"; import { BleTransport } from "@elata-biosciences/eeg-web-ble"; const transport = new BleTransport({ sourceName: "my-app", deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), logger: (msg) => console.debug("[BLE]", msg), }, }); ``` Use `onStatus` for UI state and `onFrame` for EEG data. ```ts theme={null} import type { HeadbandFrameV1 } from "@elata-biosciences/eeg-web"; transport.onStatus = (status) => { const el = document.getElementById("status"); if (el) el.textContent = status.state; }; transport.onFrame = (frame: HeadbandFrameV1) => { console.log("channels", frame.eeg.channelNames); console.log("rows", frame.eeg.samples.length); }; ``` Browser BLE flows usually need a click or another user gesture to open the device picker. ```ts theme={null} async function connectHeadband() { try { await transport.connect(); await transport.start(); } catch (err) { console.error("Connection failed:", err); } } ``` In simpler app flows, `startStreaming()` is often the safest default because it combines `connect()` and `start()` in one call. Compute band powers per channel as frames arrive. ```ts theme={null} import { band_powers } from "@elata-biosciences/eeg-web"; transport.onFrame = (frame: HeadbandFrameV1) => { const { eeg } = frame; for (let ch = 0; ch < eeg.channelCount; ch++) { const channelSamples = eeg.samples.map((row) => row[ch]); const powers = band_powers( new Float32Array(channelSamples), eeg.sampleRateHz, ); console.log( `${eeg.channelNames[ch]}: alpha=${powers.alpha.toFixed(2)} beta=${powers.beta.toFixed(2)}`, ); } }; ``` ## Full example ```ts theme={null} import { initEegWasm, band_powers, AthenaWasmDecoder, } from "@elata-biosciences/eeg-web"; import type { HeadbandFrameV1 } from "@elata-biosciences/eeg-web"; import { BleTransport } from "@elata-biosciences/eeg-web-ble"; await initEegWasm(); const transport = new BleTransport({ sourceName: "my-app", deviceOptions: { athenaDecoderFactory: () => new AthenaWasmDecoder(), logger: (msg) => console.debug("[BLE]", msg), }, }); transport.onStatus = (status) => { const el = document.getElementById("status"); if (el) el.textContent = status.state; }; transport.onFrame = (frame: HeadbandFrameV1) => { const { eeg } = frame; for (let ch = 0; ch < eeg.channelCount; ch++) { const channelSamples = eeg.samples.map((row) => row[ch]); const powers = band_powers( new Float32Array(channelSamples), eeg.sampleRateHz, ); console.log( `${eeg.channelNames[ch]}: alpha=${powers.alpha.toFixed(2)} beta=${powers.beta.toFixed(2)}`, ); } }; try { await transport.connect(); await transport.start(); } catch (err) { console.error("Connection failed:", err); } ``` ## Clean shutdown Stop the stream and release the Bluetooth session when the user leaves the view or closes the page. ```ts theme={null} async function shutdown() { await transport.stop(); await transport.disconnect(); } window.addEventListener("beforeunload", shutdown); ``` ## Handle reconnection If a disconnect is recoverable, try to reconnect and restart the stream. ```ts theme={null} transport.onStatus = (status) => { if (status.state === "disconnected" && status.recoverable) { console.log("Attempting reconnect..."); setTimeout(async () => { try { await transport.connect(); await transport.start(); } catch (error) { console.error("Reconnect failed:", error); } }, 2000); } }; ``` ## Understand the transport lifecycle | Method | What it does | | - | - | | `startStreaming()` | Connects and starts the stream in one call | | `connect()` | Opens the Bluetooth picker and prepares the session | | `start()` | Begins the EEG stream and triggers `onFrame` | | `stop()` | Stops the stream but keeps the Bluetooth session open | | `disconnect()` | Releases the Bluetooth session | ## Device notes Works with the standard browser BLE flow. Use `athenaDecoderFactory` during transport creation. Athena devices expose richer frame content, including 8 EEG channels plus additional sensor data. You can also inspect metadata after connecting: ```ts theme={null} await transport.connect(); const isAthena = transport.getIsAthena(); const boardInfo = transport.getBoardInfo(); const channelNames = transport.getEegNames(); ``` If you are testing across Muse variants, keep the Athena decoder factory enabled from day one so you do not need a separate transport setup later. ## Architecture at a glance The browser EEG BLE flow usually looks like this: 1. initialize `@elata-biosciences/eeg-web` 2. create a `BleTransport` 3. connect from a user gesture 4. receive `HeadbandFrameV1` frames 5. compute app metrics such as band powers, scores, or state transitions 6. render charts, scores, or adaptive UI in your app ## Integration tips * Buffer frames before analysis for more stable results * Use `Float32Array` when calling `band_powers` * Keep BLE connection logic behind explicit user actions * Move frame handling into app state instead of leaving it in `console.log` * Start with a demo app if you need a working reference project first * Test with synthetic data first during development if no headband is available `band_powers` is most useful with roughly 1 to 2 seconds of data. At 256 Hz, that is about 256 to 512 samples. ## Where to go next Review device behavior, protocol details, and compatibility notes. See the transport package API and platform caveats in more detail. Follow the tutorial version of this workflow with app-oriented steps. Integrate browser EEG processing into an app before adding BLE transport. Learn the frame schema and transport boundary used by EEG flows. Pair this EEG workflow with the browser camera pipeline when you need multimodal biometrics. # EEG in a browser App Source: https://docs.elata.bio/sdk/guides/eeg-browser Add Elata EEG WASM APIs to an existing browser application. Use this page if you want the package model for browser EEG, not a full integration walkthrough. If you want numbered steps for an existing app, use [Add EEG To An Existing Browser App](/sdk/tutorials/eeg-existing-app). If you want the scaffold path, use [Build Your First Elata App](/sdk/tutorials/first-app). If you want a running app without manual wiring, scaffold with [create-elata-demo](/sdk/create-elata-demo) first. EEG is **optional** in the usual Elata journey: **camera rPPG** is the primary browser integration for many apps. Add `eeg-web` when you need brain-signal analysis; add **`eeg-web-ble`** alongside it when you need **Web Bluetooth** for a Muse-compatible headset. EEG measures electrical activity from the brain using a wearable headset. In browser apps, developers usually use EEG to stream headset samples, compute features such as band powers, and drive feedback, dashboards, adaptive UX, or interactive experiences. Concrete app examples include: * meditation or breath-training apps that adapt guidance to focus or calmness * neurofeedback experiences that reward attention or steadiness * simulated athletic-performance or training apps that react to mental state * games or creative tools that change difficulty, pacing, or effects based on live EEG features *** ## Install ```bash pnpm theme={null} pnpm add @elata-biosciences/eeg-web ``` ```bash npm theme={null} npm install @elata-biosciences/eeg-web ``` *** ## What `eeg-web` Gives You `@elata-biosciences/eeg-web` provides: * browser-side EEG WASM initialization * signal-processing and model exports such as `band_powers` * shared types and contracts used by higher-level browser integrations This package does not handle Bluetooth device connection by itself. Add `@elata-biosciences/eeg-web-ble` if you also need browser BLE transport. *** ## Minimal Integration ```ts theme={null} import { initEegWasm, band_powers } from "@elata-biosciences/eeg-web"; await initEegWasm(); const eegData = new Float32Array([0, 1, 0, -1]); const powers = band_powers(eegData, 256); console.log("alpha", powers.alpha); ``` *** ## Typical Integration Flow 1. Initialize the packaged WASM runtime with `initEegWasm()`. 2. Pass browser-side EEG sample buffers into the exported analysis functions. 3. If you later need live device transport, combine this package with `eeg-web-ble`. *** ## When To Use The EEG Template Instead Prefer the scaffolded `eeg-demo` template when you want: * a known-good Vite setup * a reference for how bundled WASM assets should be served * a synthetic-data app that runs without hardware *** ## Common Gotchas * `band_powers()` takes a **single-channel** `Float32Array`, not `number[]`. `frame.eeg.samples` is laid out as `samples[sampleIdx][channelIdx]` (one row per time step), so extract a channel with `getEegChannelSamples(frame, channelIdx)` from `eeg-web`. Passing a plain array will cause a WASM runtime error. For a Muse headband (4 channels: TP9, AF7, AF8, TP10), use a frontal channel (AF7 = index 1, or AF8 = index 2) for cognitive state features, or average across channels. * `WasmCalmnessModel.process()` expects **interleaved** samples: `[s0_ch0, s0_ch1, s1_ch0, s1_ch1, ...]`. Use `getEegInterleavedSamples(frame)` from `eeg-web` to get that layout. * `WasmCalmnessModel` needs `channelCount` at construction, but `channelCount` only arrives on the first frame. Construct the model inside your first-frame handler. * If `initEegWasm()` fails, your app may not be loading the packaged `wasm/` assets correctly. * If you need a live headset connection, `eeg-web` alone is not enough. * If you are only evaluating the SDK, the scaffolded app is faster than manual setup. *** ## Next Step-by-step integration tutorial Connect a Muse headset Package API and exports Common failures and fixes # Example Applications Source: https://docs.elata.bio/sdk/guides/example-apps Open source browser apps built with Elata EEG, Web Bluetooth, and rPPG packages. These are full product-shaped integrations with routing, sessions, charts, and game loops. They go beyond the `create-elata-demo` starter templates and show what a complete app looks like. ## Featured Apps Guided breathing with live EEG and rPPG biometric monitoring. 3D flight simulator with post-session neural analytics. Brain-reactive arcade gameplay that responds to mental state. Chess against Stockfish with a neural HUD overlay. Stress-modulated reaction game with results tracking. *** ## What These Apps Demonstrate Each app is a standalone repository that uses the published `@elata-biosciences` packages: | Package | Purpose | | - | - | | `@elata-biosciences/eeg-web` | EEG signal processing and models | | `@elata-biosciences/eeg-web-ble` | Web Bluetooth headband streaming | | `@elata-biosciences/rppg-web` | Camera-based heart rate measurement | These apps are useful as reference implementations when you need to see how a real product wires up SDK sessions, state management, charts, and cleanup. *** ## When To Use These vs. `create-elata-demo` | If you want to... | Start here | | - | - | | Get a working app in under a minute | [`create-elata-demo`](/sdk/create-elata-demo) | | See a complete product with routing, game loops, or data visualization | One of the example apps above | | Compare your integration against a known-good reference | Either path works | *** ## Next Scaffold a starter app Package decision guide Primary browser integration # Remote sensing in a browser App Source: https://docs.elata.bio/sdk/guides/rppg-browser Add browser-based camera rPPG with the supported Elata session helpers. Use this page if you want the integration model for browser rPPG. If you want numbered steps for an existing app, use [Add Camera-Based rPPG To An Existing Browser App](/sdk/tutorials/rppg-existing-app). If you want the scaffold path, use [Build Your First Elata App](/sdk/tutorials/first-app). This is the **primary** browser biosignal path for most products: no headset required before you ship meaningful signal UX. rPPG stands for remote photoplethysmography. It estimates pulse-related changes from camera video, usually from a face region, without requiring a wearable sensor. In browser apps, developers usually use rPPG to turn a live camera stream into heart-rate-style metrics, diagnostics, and wellness-oriented feedback. Concrete app examples include: * deception or bluffing games that react to pulse changes during key moments * stress or arousal feedback in training and social experiences * breathing and relaxation flows that show physiological response over time * biofeedback-oriented health or wellness apps that want camera-based pulse signals without extra hardware *** ## Install ```bash pnpm theme={null} pnpm add @elata-biosciences/rppg-web ``` ```bash npm theme={null} npm install @elata-biosciences/rppg-web ``` *** ## Recommended vs. Advanced Use `createRppgSession()` for browser apps. It handles WASM init, frame capture, ROI orchestration, diagnostics, and cleanup. **Recommended:** * Use `createRppgSession()` for browser apps. * Use `createManagedRppgSession()` when you want built-in restart behavior after a terminal processor failure. * Use `createRppgPipeline()` from `@elata-biosciences/eeg-web` only if you intentionally need low-level sample ingestion. **Advanced:** * Use `RppgProcessor`, `DemoRunner`, custom backends, or generated WASM bindings only if you need custom orchestration and understand the runtime lifecycle already. * If you are not debugging the SDK itself, do not start with generated WASM exports. *** ## Minimal Integration ```ts theme={null} import { createRppgSession } from "@elata-biosciences/rppg-web"; const session = await createRppgSession({ video: videoEl, sampleRate: 30, backend: "auto", faceMesh: "off", onDiagnostics: (diagnostics) => { console.log(diagnostics.state.status, diagnostics.faceTrackingMode); console.log(diagnostics.framesSeen, diagnostics.totalSamplesReceived); console.log(diagnostics.issues, diagnostics.processorFailure); }, onError: (error) => { console.error(error.code, error.message); }, }); console.log(session.getMetrics()); ``` *** ## Typical Integration Flow 1. Acquire a camera stream in the browser and attach it to a `video` element. 2. Call `createRppgSession({ video, backend: "auto" })`. 3. Read metrics from `session.getMetrics()`. 4. Surface diagnostics from `onDiagnostics` or `session.getDiagnostics()`. 5. Stop the session during cleanup with `await session.stop()`. `createRppgSession()` owns WASM init, FaceMesh loading, frame scheduling, ROI selection, diagnostics, and cleanup. Use `session.state` or `diagnostics.state` to distinguish: * normal `running` * startup fallback or degraded setup via `degraded` * terminal runtime processor failure via `failed` If you intentionally choose `faceMesh: "off"`, the session stays in supported `video_frame` mode and is not reported as a FaceMesh failure by default. If your app needs explicit asset paths instead of the default `/pkg/*` lookup, pass one or more of: ```ts theme={null} const session = await createRppgSession({ video: videoEl, wasmJsUrl: "/assets/rppg_wasm.js", wasmBinaryUrl: "/assets/rppg_wasm_bg.wasm", }); ``` Advanced apps can also provide `wasmImporter` directly. *** ## Managed Restart Flow If you want the SDK to own restart timing after terminal processor failures, use the managed wrapper: ```ts theme={null} import { createManagedRppgSession } from "@elata-biosciences/rppg-web"; const managed = await createManagedRppgSession({ video: videoEl, faceMesh: "off", maxRetries: 3, retryDelayMs: 1500, onStateChange: (state) => { console.log(state.status, state.retryCount, state.lastError?.code); }, }); ``` The managed wrapper exposes high-level states such as `starting`, `running`, `retrying`, and `failed`, while still letting apps drop down to the underlying `RppgSession` when needed. *** ## Advanced Helpers Use `getTraceSnapshot()` when you need recent waveform or debug points for charts, debug panels, or regression capture: ```ts theme={null} const trace = session.getTraceSnapshot(300); console.log(trace.points); console.log(trace.lastSample); console.log(trace.backendFailure); ``` For peak/threshold waveform debug from the trace data, use `computeTraceWaveformDebug()`: ```ts theme={null} import { computeTraceWaveformDebug } from "@elata-biosciences/rppg-web"; const waveform = computeTraceWaveformDebug(session.getTraceSnapshot(300)); console.log(waveform.peaks); ``` Use `normalizeRppgError()` instead of parsing raw message text in app code: ```ts theme={null} import { normalizeRppgError } from "@elata-biosciences/rppg-web"; const normalized = normalizeRppgError(session.lastError, session.getDiagnostics()); console.log(normalized?.code); console.log(normalized?.message); console.log(normalized?.guidance); ``` This gives apps stable categories such as `wasm_init_failed`, `backend_unavailable`, `camera_not_playing`, and `processor_failed`. If you want a single app-facing snapshot with restart status, publish gating, trace data, and stable messages, use `createRppgAppAdapter()`: ```ts theme={null} import { createManagedRppgSession, createRppgAppAdapter, } from "@elata-biosciences/rppg-web"; const managed = await createManagedRppgSession({ video, faceMesh: "off", }); const adapter = createRppgAppAdapter(); const app = adapter.getSnapshot(managed); if (app.canPublish) { console.log(app.publishBpm); } console.log(app.status, app.message); ``` If you want the SDK to own the recurring snapshot loop too, use `createRppgAppMonitor()`: ```ts theme={null} import { createManagedRppgSession, createRppgAppMonitor, } from "@elata-biosciences/rppg-web"; const managed = await createManagedRppgSession({ video, faceMesh: "off", }); const monitor = createRppgAppMonitor(managed, { intervalMs: 500 }); monitor.subscribe((snapshot) => { console.log(snapshot.status, snapshot.publishBpm); }); monitor.start(); ``` `createRppgSession()` now waits for the video element to start playing by default. If you need to coordinate that step yourself, call `ensureVideoPlaying()` directly: ```ts theme={null} import { ensureVideoPlaying } from "@elata-biosciences/rppg-web"; await ensureVideoPlaying(video, { timeoutMs: 5000 }); ``` *** ## When To Use The rPPG Template Instead Prefer the scaffolded `rppg-demo` template when you want: * a known-good browser camera app * a reference for packaged WASM asset loading * a faster comparison point when debugging your own integration *** ## Common Gotchas * If `session.backendMode` is `unavailable`, your app is probably not serving the packaged `pkg/` assets correctly. * If `session.state.status` is `failed`, treat that processor backend as terminal and recreate the session instead of continuing to poll metrics from it. * If you see "backend pipeline has no push\_sample API", you likely bypassed the safe wrapper path. Start with `createRppgSession()` for browser apps, or `initEegWasm()` plus `createRppgPipeline()` for low-level ingestion. * If you hit `wasmrppgpipeline_new`, initialize the WASM module before creating low-level pipelines and avoid calling generated constructors directly. * If you see deprecated init warnings, route startup through `initEegWasm()` instead of forwarding raw strings, URLs, or buffers to the generated init exports. * If camera access fails, confirm the page has permission to use `getUserMedia`. * If `session.lastError` is non-null, use its `code` and `message` to surface the real capture or processor failure instead of retrying blindly. * If you are just evaluating the SDK, the scaffolded app is much faster than building the whole browser pipeline yourself. *** ## Version Guidance The packages are versioned independently, so `rppg-web` and `eeg-web` won't share a version number, and the latest releases aren't automatically compatible with each other. If you use several Elata packages together, use the set `create-elata-demo` pins. See [Compatibility](/sdk/operations/compatibility#package-versions). *** ## Next Step-by-step rPPG integration Package API and exports Common failures and fixes # Camera Integration Source: https://docs.elata.bio/sdk/guides/rppg-camera Add camera-based heart rate to a browser app with the Elata rPPG Web SDK. Use this guide when you want lower-level control over camera-based heart rate in a browser app. For the recommended high-level API, start with [rPPG in a browser app](/sdk/guides/rppg-browser). ## What this guide covers This guide shows how to: * capture webcam frames in the browser * detect a face and extract a face region * feed samples into the rPPG processor * read live BPM and signal quality * improve estimates with Muse PPG when available ## Before you start You need: * `@elata-biosciences/rppg-web` * a browser with camera access (`getUserMedia`) and WebAssembly support * `https://` or `localhost` ```bash npm theme={null} npm install @elata-biosciences/rppg-web ``` ```bash pnpm theme={null} pnpm add @elata-biosciences/rppg-web ``` rPPG works best when the face is well lit, mostly still, and fully visible in frame. Most apps should use `createRppgSession()`, which wraps everything on this page (WASM init, frame capture, ROI selection, diagnostics, cleanup). See [rPPG in a browser app](/sdk/guides/rppg-browser). Use the lower-level pieces below only when you need custom orchestration. ## Choose a starting point Scaffold a working browser app if you want the fastest path to a reference implementation. Learn the package entry points and the higher-level browser API. See the lower-level camera and face-detection primitives used in this guide. Add Muse-based calibration when you want stronger estimates. ## Quick integration with DemoRunner `DemoRunner` handles frame capture, face detection, ROI extraction, and processing in one flow, while leaving you in control of the processor. ```ts theme={null} import { RppgProcessor, MediaPipeFrameSource, DemoRunner, loadWasmBackend, } from "@elata-biosciences/rppg-web"; const video = document.querySelector("video")!; // a playing camera