# Store and serve large media with Hyperblobs



<Callout type="info">
  **This guide focuses on the Pear/Bare logic.** It shows [`hyperblobs`](https://www.npmjs.com/package/hyperblobs) and [`hypercore-blob-server`](https://www.npmjs.com/package/hypercore-blob-server) on their own—no Electron, no UI. For the same blob plumbing wired into full desktop apps, see [Stream stored video in a peer-to-peer app](/p2p/how-to/stream-and-share-media/stream-stored-video-in-a-peer-to-peer-app), [Stream a live camera in a peer-to-peer app](/p2p/how-to/stream-and-share-media/stream-a-live-camera-in-a-peer-to-peer-app), and [Back up photos in a peer-to-peer app](/p2p/how-to/stream-and-share-media/back-up-photos-in-a-peer-to-peer-app). The blob logic is identical across all three; only the UI and the frame/file source change. To share a whole folder of files over [Hyperdrive](/p2p/reference/building-blocks/hyperdrive) instead, see [Share files in a peer-to-peer app](/p2p/how-to/stream-and-share-media/share-files-in-a-peer-to-peer-app).
</Callout>

A [Hypercore](/p2p/reference/building-blocks/hypercore) is an append-only log of small blocks—great for messages, awkward for a 200 MB video. [`Hyperblobs`](https://www.npmjs.com/package/hyperblobs) solves that: it chunks arbitrarily large binary data across a Hypercore and hands back a small **blob id** that addresses it. [`hypercore-blob-server`](https://www.npmjs.com/package/hypercore-blob-server) then serves any blob over local HTTP (`127.0.0.1`) so a `<video>` or `<img>` tag—or any HTTP client—can stream it **with range requests**, fetching only the bytes it needs.

This is purely Pear-end logic: it runs in a [Bare](/bare/reference/bare/modules) worker (or any Bare/Node process) and never touches a UI.

<Callout type="info">
  **Pear-end logic—start from a boilerplate.** The code in this guide lives in the Bare [worker](/pear/explanation/workers): peer-to-peer logic, no UI. The same worker runs unchanged on desktop, terminal, and mobile—only the shell differs (see [Runtime and languages](/pear/explanation/runtime-and-languages)). Start from a boilerplate in [Start from a template](/p2p/getting-started/from-a-template)—desktop ([`hello-pear-electron`](/p2p/getting-started/from-a-template/start-from-hello-pear-electron)) or terminal ([`hello-pear-bare`](/p2p/getting-started/from-a-template/start-from-hello-pear-bare))—and add this capability on top.
</Callout>

## How addressing works [#how-addressing-works]

Writing a blob returns an **id** describing where the bytes live in the core:

```js skip="id-shape"
{ byteOffset, blockOffset, blockLength, byteLength }
```

Combine that id with the blobs **core key** and you have everything a peer needs to fetch the blob—directly as bytes, or through a blob-server link.

## Add the dependencies [#add-the-dependencies]

```sh skip="deps"
npm install corestore hyperswarm hyperblobs hypercore-blob-server hypercore-id-encoding
```

## Write and seed a blob [#write-and-seed-a-blob]

The writer:

* opens a [Corestore](/p2p/reference/helpers/corestore) namespace and wraps it with `Hyperblobs` (L12–L13)
* announces the blobs core on [Hyperswarm](/p2p/reference/building-blocks/hyperswarm) so readers can find it (L16)
* streams a local file through `createWriteStream()` (L18–L24)
* and prints the `{ key, ...id }` payload other peers need (L27–L28):

```javascript file=<rootDir>/examples/how-to/stream-and-share-media/store-and-serve-large-media-with-hyperblobs/writer-app/index.js title="writer-app/index.js" lineNumbers {12-13,16,18-24,27-28} skip="example-import"
import Corestore from 'corestore'
import Hyperswarm from 'hyperswarm'
import Hyperblobs from 'hyperblobs'
import idEnc from 'hypercore-id-encoding'
import fs from 'bare-fs'
import process from 'bare-process'

const store = new Corestore('./writer-store')
const swarm = new Hyperswarm()
swarm.on('connection', (conn) => store.replicate(conn))

const blobs = new Hyperblobs(store.get({ name: 'blobs' }))
await blobs.ready()

// Announce the blobs core so readers can discover and replicate it.
swarm.join(blobs.core.discoveryKey, { server: true, client: false })

// Store a local file as a blob.
const ws = blobs.createWriteStream()
fs.createReadStream('./clip.mp4').pipe(ws)
await new Promise((resolve, reject) => {
  ws.on('error', reject)
  ws.on('close', resolve)
})

// Share this with readers — the core key plus the blob id.
const blob = { key: idEnc.normalize(blobs.core.key), ...ws.id }
console.log('blob:', JSON.stringify(blob))

// Keep seeding until interrupted, then tear down cleanly.
process.once('SIGINT', async () => {
  await blobs.close()
  await swarm.destroy()
  await store.close()
  process.exit(0)
})
```

## Read or serve the blob on another peer [#read-or-serve-the-blob-on-another-peer]

The reader:

* parses that JSON blob descriptor from the command line (L9)
* opens the remote core by key and replicates it (L15–L17)
* **Option A** pulls the raw bytes with `blobs.get()` and the id fields (L20–L27)
* **Option B**—what desktop apps use—starts [`hypercore-blob-server`](https://www.npmjs.com/package/hypercore-blob-server) and prints a local `link` the renderer can pass to `<video>` or `<img>` (L30–L33)

  Option B is what desktop apps use: the worker hands the `link` to the renderer, and the browser streams the media straight from `hypercore-blob-server`, requesting byte ranges as the user scrubs.

```javascript file=<rootDir>/examples/how-to/stream-and-share-media/store-and-serve-large-media-with-hyperblobs/reader-app/index.js title="reader-app/index.js" lineNumbers {9,15-17,20-27,30-33} skip="example-import"
import Corestore from 'corestore'
import Hyperswarm from 'hyperswarm'
import Hyperblobs from 'hyperblobs'
import BlobServer from 'hypercore-blob-server'
import idEnc from 'hypercore-id-encoding'
import process from 'bare-process'

// { key, byteOffset, blockOffset, blockLength, byteLength }
const blob = JSON.parse(process.argv[2])

const store = new Corestore('./reader-store')
const swarm = new Hyperswarm()
swarm.on('connection', (conn) => store.replicate(conn))

const core = store.get({ key: idEnc.decode(blob.key) })
await core.ready()
swarm.join(core.discoveryKey, { client: true, server: false })

// Option A — read the raw bytes.
const blobs = new Hyperblobs(core)
const bytes = await blobs.get({
  byteOffset: blob.byteOffset,
  blockOffset: blob.blockOffset,
  blockLength: blob.blockLength,
  byteLength: blob.byteLength
})
console.log('read bytes:', bytes.byteLength)

// Option B — serve over local HTTP so a <video>/<img> can stream it with range requests.
const server = new BlobServer(store.session())
await server.listen()
const link = server.getLink(blob.key, { blob, type: 'video/mp4' })
console.log('stream from:', link)

// Close the server and blobs before the swarm and store on exit.
process.once('SIGINT', async () => {
  await server.close()
  await blobs.close()
  await swarm.destroy()
  await store.close()
  process.exit(0)
})
```

## Tear it down [#tear-it-down]

Each app cleans up on `Ctrl+C`: the `SIGINT` handler at the end of every file closes the blobs (and, in the reader, the HTTP server) **before** the swarm and store, so in-flight reads and the local server shut down before the underlying core does.

## See also [#see-also]

* [Share files in a peer-to-peer app](/p2p/how-to/stream-and-share-media/share-files-in-a-peer-to-peer-app)—replicate a folder of files over [Hyperdrive](/p2p/reference/building-blocks/hyperdrive) instead of chunking individual blobs.
* [Stream stored video in a peer-to-peer app](/p2p/how-to/stream-and-share-media/stream-stored-video-in-a-peer-to-peer-app)—this primitive behind a desktop video player.
* [Stream a live camera in a peer-to-peer app](/p2p/how-to/stream-and-share-media/stream-a-live-camera-in-a-peer-to-peer-app)—the same blobs, fed by live camera frames.
* [Back up photos in a peer-to-peer app](/p2p/how-to/stream-and-share-media/back-up-photos-in-a-peer-to-peer-app)—decode images with `bare-ffmpeg` before storing them as blobs.
* [From append-only logs to files](/p2p/explanation/from-logs-to-files)—how Hypercore, Hyperblobs, and Hyperdrive relate.
* [Hypercore reference](/p2p/reference/building-blocks/hypercore)—the append-only log Hyperblobs chunks data across.
