# Save a plot. Reproduce it.

Scene JSON binds a display to its source LAS and records the applied settings without embedding the measurement columns.

Danila Karnaukh | v4 | 2026-09-29

## The browser workflow

- Load a LAS and configure the plot.
- Click Save settings to download a `.scene.json` file.
- In another session, load the same LAS in the same tool, then click Load settings.
- Click Verify plot to inspect the applied configuration and sample checks; export the PNG.

A settings file contains the filename, fingerprint, row count, curve references and plotting choices. Crossplot settings also contain selected source-row indices. It does not contain raw curve arrays. Each tool has a separate in-memory dataset; switching tools does not transfer your LAS.

## Binding and validation

The format is `petrophysics.scene/v1`. The dataset fingerprint is SHA-256 over the decoded source text encoded as UTF-8, with a leading BOM removed and CRLF/CR normalized to LF. This is a normalized-text fingerprint, not a checksum of the original file bytes. Other whitespace and header edits change it. HTTPS, localhost or a browser context supporting Web Crypto is required for scene commands.

Curve references contain `id`, `mnemonic` and `unit`. IDs are zero-based LAS column indices. Duplicate mnemonics remain distinct. A changed unit, wrong source, unknown setting or invalid limit is rejected before application. Configuration is a full replacement, not a partial patch; repeated application does not add duplicate curves.

## In-page JavaScript API

A browser client that is permitted to execute page JavaScript can call `window.Petrophysics`. This is a local page API, not an HTTP endpoint, a registered WebMCP provider or a remote MCP server.

```
const api = window.Petrophysics;
const capabilities = api.get_capabilities();
const dataset = await api.inspect_dataset();
const scene = await api.get_scene();
// Edit the returned full configuration, then validate and apply it.
const result = await api.apply_scene(scene);
const checked = await api.inspect_scene();
await api.export_result({format: 'png'});
await api.export_result({format: 'json'});
```

| Command | Input / result |
| --- | --- |
| get_capabilities() | Synchronous catalog; currentTool and commandsAvailableHere identify this page. |
| import_las({text, filename}) | Parses the supplied LAS text and returns dataset metadata. Browser file chooser also works. No arbitrary URL fetch. |
| inspect_dataset() | Filename, source SHA-256, row count, warnings and each curve’s ID, unit, range, valid/missing/non-positive counts. |
| get_scene() | Returns the full scene JSON. |
| apply_scene(scene) | Checks source and settings, applies the scene, then returns inspect_scene. |
| configure_crossplot(config) | Crossplot page only. Apply a complete crossplot configuration to the current dataset. |
| configure_log_view(config) | Log Viewer page only. Apply a complete four-track configuration. |
| set_selection({rows, operation, colour?}) | Crossplot only. Replace/Add/Remove explicit zero-based numeric source rows. Not depth values. |
| fit_regression({type, width?}) | Crossplot only. Fit selected rows intersected with the filter. Rejects an invalid fit without replacing it. |
| inspect_scene() | Applied scene, release, warnings and numeric display checks. Regression result includes success/failure details. |
| export_result({format, download?}) | png or json; download defaults to true. Returns {filename, format, bytes, blob}; PNG adds width/height. |
| close_dataset() | Clears the current page’s dataset and display. |

All methods except get_capabilities return Promises. A concurrent mutating API request receives BUSY. Wait for a command before the next. API errors have a code when supplied by the scene layer and always include a message; parser/display errors may be plain Errors.

## Configuration details

Crossplot: `x`/`y` each have curve, type, min and max. Limits can be null (Auto). `view` is null for the configured full range or an object with xmin/xmax/ymin/ymax. Y min is the bottom endpoint. Points, colouring, filter, selection and regression are separate objects. `selection.rows` can include numeric pairs outside the current viewport or log-scale domain; regression fits original values, subject to the filter and model’s domain requirements.

Log Viewer: `depth` names the detected index and ordered top/bottom within its range. `tracks` contains exactly four objects in left-to-right order, each with curves and pairFill. Every curve includes its own scale, style and baselineFill. Pair `a`/`b` are LAS curve IDs belonging to that track. Fill opacity is 0–1 in JSON; the UI shows percent. Inactive pairs may use null references on empty tracks.

## Use concrete examples

[Download the JSON Schema](../../agents/schemas/scene-v1.json) · [Four-track scene](../../examples/synthetic-gas-oil-water/four-track.scene.json) · [Crossplot scene](../../examples/synthetic-gas-oil-water/density-neutron.scene.json). The runtime additionally checks dataset identity, units, numerical domains and pair references.

PNG exports capture the current display with legends at 2× composition resolution. They are not vector files. Pixel appearance depends on viewport, device pixel ratio and available fonts. The JSON and numerical checks are the record for reproducibility.
