see the wholesection. load onlywhat you see.

Ten gigabytes. Thirty milliseconds. One browser tab.

scroll to go beneath

a 6 GB line, live in this page.

Drag it, zoom it, switch its colormap.Every tile you see was fetched for the view you are looking at, nearest the centre first.

north_survey / inline_2048.sgy6.00 GB250,000 traces6,000 samplesfloat32synthetic
idlelevel –tiles –in flight 0fetched 0 MBof 6.00 GBframes 0drag⌘ scrolldouble-click
512²cells in every tile, uploaded as a float texture
6tiles in flight, nearest the centre first
256MBtile cache, least recently used by bytes
2frames unwanted, and a request is cancelled
0frames drawn while nothing changes

a 6 GB line doesn't fit through a browser.

Fetching a whole SEG-Y file before drawing it stalls exploration.A 1 GB file took tens of seconds to arrive and left the tab unresponsive; real files are 5 to 10 GB, reached over the internet. Outkrop asks only for what the pane can show.

Download, then plot0.0 s0 MB of 6.00 GB arrived · first pixel after ≈ 4 min
Outkrop0.00 s4 MB · the tiles in view, then sharper ones as you zoom
Illustrative: transfer time only, over a 200 Mbit/s link.
1 MB of the filedownloadedOutkrop's first view

long waits became milliseconds.

Server time no longer grows with file size.Measured on real files read cold from disk: the same actions, against whole-file fetching and against Outkrop with and without its cache.

655×faster to open a 10 GB line
6.4%cache size: 686 MB for 10 GB, built in about 37 s
0.9 sruntime start, once; no action starts a container
  1. Open a 10 GB line655×
  2. Zoom to 10% of it491×
  3. Pan half a screen982×
  4. Pick a shot in 10 GB of gathers506×
  5. Open a 48,001-sample LAS1,150×
  6. Tie check350×
Server time, medians, files read cold from disk on gpu-06. A LAS cache is about the file's size. Tie verdicts match the per-request check.

move only what the screen can show.

the whole line

A 1,600-pixel pane can't show more than 1,600 traces. Outkrop picks the coarsest level that still fills the pane's pixels, and the whole line arrives in a handful of tiles.

closer

Every step in halves the cells per tile. Tiles nearest the centre come first. Until they land, the coarser parent stands in, so the view sharpens instead of flashing.

every sample

At level zero you are reading the file's own samples. Once the traces are far enough apart, wiggles join the variable density.

on screen–
fetched–
whole file6.00 GB
idlelevel –tiles –in flight 0fetched 0 MB

every level halves the one below.

The dispatcher keeps each file as a pyramid.Levels zero to three are read from the file itself; level four and up come from a cache it builds beside it, where one cell stands for 256 of the file's. The transfer follows the extent and resolution in view, not the size of the file.

lvltraces × samplessizeread from

For a 6 GB line, around the spot the dive above ended on. Each step halves the traces and the samples, so the bytes fall by four.

sections, logs, crossplots.
one engine.

Images, wiggles, curves, points, markers, axes and colorbars are layers in the same engine.So a section, the well that crosses it and the crossplot of that well can share a page without fighting over the GPU.

Seismic sections

Variable density with wiggle traces, across a fault. Well A runs through it.

Well logs

Well A's curves in depth tracks, on linear and logarithmic scales.

Crossplots

Density against neutron around well A, coloured by gamma ray.

nothing draws while nothing changes.

A camera move, a tile arriving, a style change: each asks for a frame.Every ask before the next animation frame draws once. An idle view costs nothing.

  • Fetch6 at a time, centre first
  • Canceldropped after 2 unwanted frames
  • Retrywaits 5 s after a failure
  • Changesfile changed: fetch again
  • Precisiondrawn from an origin near the data
  • Recoveryrebuilds after losing WebGL
Try itmove over the section and watch the counters
asks 0 · draws 0idle
requestRenderframe drawnlast 3 s

takes its colours from your page.

Views read --outkrop-* custom properties from any ancestor and follow them when a class, attribute or the system appearance changes.What the page leaves unset comes from the text colour the view inherits.

every axis on the page follows
theme.css
:root {
--outkrop-text: var(--gray-12);
--outkrop-grid: var(--gray-a4);
--outkrop-font: 10px monospace;
}
:root.dark {
--outkrop-plot: #111;
}

/* or one view at a time */
<CrossplotView theme={{ text: '#222' }} />

from a dispatcher to a section.

An engine owns panes; panes own a camera, layers and overlays. The data worker decodes off the main thread, and the React surface handles the canvas, resizing and the WebGL fallback.

Dispatchertiles over HTTP
Data workerdecodes off the main thread
OutkropSurface · canvas, resize, WebGL fallback
Engine
Pane
CameraLayersOverlays
main.tsx
import { AxesOverlay, Camera, DataWorker, HttpDataSource,
       ImageTileLayer, seismicImage, type Engine } from '@bs/outkrop-core'
import { OutkropSurface } from '@bs/outkrop-react'

// A section from a dispatcher
const source = new HttpDataSource({ baseUrl: '/services/dispatcher' })
const worker = new DataWorker()

async function setup(engine: Engine) {
const info = await source.seismicInfo('/data/line.sgy')
const camera = new Camera({ x: [0, info.traces], y: [0, info.samples] })
const pane = engine.addPane({ id: 'section', rect: { x: 48, y: 8, width: 600, height: 400 }, camera })

pane.add(new ImageTileLayer({
  image: seismicImage(source, info, { worker }),
  clip: [-1, 1],
  colormap: 'Seismic',
}))
pane.add(new AxesOverlay({ x: { title: 'Trace' }, y: { title: 'Sample' } }))
}

export const Section = () => <OutkropSurface setup={(engine) => void setup(engine)} />

try it yourself.
on your own data.

Bring your own SEG-Y. Explore it on your device.No install. No sign-in. No upload. Drop it in, zoom to the last sample, export what you see.

Drop it here.Open your own file.Let go.

SEG-YSULASCSVJSON

before you ask.

More in the usage guide and the documentation index.

Which files can it show?
SEG-Y sections, and LAS and DLIS logs, served by the dispatcher. Crossplots draw any two curves against each other, with an optional colour column.
Can I use my own data source?
Yes. HttpDataSource talks to a dispatcher, and an application can supply its own data source instead. The data worker decodes and processes either way.
What does the dispatcher need to serve?
The /domain/v2 routes frozen in domain-v2.openapi.json. It reads levels zero to three from the file and builds level four and up in a cache under .derived/viz.
Does it work with server rendering?
Core imports are safe on the server; DOM and WebGL work starts when an engine is created. OutkropSurface renders an empty box on the server and is marked 'use client', so a Next.js server component can render it.
Which React versions?
React 18.3 or 19, with @bs/outkrop-core as a peer dependency of @bs/outkrop-react.
What if the browser has no WebGL2?
The surface renders the fallback you give it. If the browser takes the context away later, the engine waits for it to come back and every layer rebuilds on its next draw.

go beneath
the surface.

$ npm install @bs/outkrop-core @bs/outkrop-react