# Build with Open Waves Water

The Open Waves game is the live showcase. The Open Waves wave solver and original environmental rendering modules, packaged for independent Three.js applications. `FullWaterRenderer` assembles the game's water shaders, camera-centered water grid and horizon, procedural textures, sky, original weather, foam, breaking spray, reflected objects, caustics and underwater shading. `OceanSimulation` runs its crest propagation, shoaling, breaking and water-flow solver. The default scene uses the original Reef Pass bathymetry and environmental configuration. No on-water models, foiling controls or game engine are included.

This guide covers the 0.1 SDK API. Use the README, declarations, build manifest and license included with your downloaded release as its version-specific reference. Each package records its game commit and framework revision. The complete game, craft controls, equipment, multiplayer and account services remain outside the SDK.

## Get your purchase

1. Open [My software](https://openwaves.world/developers/downloads/) and enter the email used at checkout.
2. Request a six-digit email code, then enter the most recently requested code. Your beta game password and invitation are not used.
3. Open your license, enter the individual or legal entity receiving the license, and name the application for a Commercial license. Open the order-specific saved terms, then accept those software and hosted access terms. The saved order terms may differ from the current public website version.
4. Download an available release and your license certificate. Save the package, certificate and included license terms in a private location. The certificate records your accepted terms version and integrity hash alongside acquired release information.

Payment must be confirmed before a license appears. A checkout return alone does not prove payment succeeded. If a new purchase is missing, refresh after confirmation arrives, check that you used the checkout email and [contact Open Waves](https://openwaves.world/contact/) with your receipt reference. Do not pay again solely because delivery is pending. Never send a card number, sign-in code or API secret in a support message.

## Choose the right license

| Scope | One-time | Monthly subscription | Annual subscription |
| --- | --- | --- | --- |
| Personal, one individual’s noncommercial projects | $100 | $100 first month, then $20/month | $280 first year, then $180/year, paid upfront |
| Commercial, one named application | $200 | $200/month | $1,800/year, paid upfront |

All prices are USD before applicable taxes. Personal Monthly includes an $80 initial fee plus the first $20 monthly charge, for $100 at checkout. Personal Annual includes a $100 initial fee plus the first $180 annual charge, for $280 at checkout. Subsequent renewals are $20/month or $180/year respectively. One-time purchases include the purchased SDK version and no future updates. Subscriptions include released SDK updates during their paid term. Annual pricing is one charge per year, not monthly installments. No specific update schedule is promised.

A subscription ending stops access to future updates; you may continue using your last lawfully downloaded version under the same Personal or Commercial scope. Keep private backups. Perpetual use rights do not promise indefinite hosting of every historical download. A subscription does not convert Personal rights into Commercial rights. Client work, business use, paid products, advertising and sponsorship require Commercial licensing.

Use the billing button in [My software](https://openwaves.world/developers/downloads/) to update your payment method or cancel subscription renewal. To stop renewal, complete cancellation in billing management and check the resulting status and paid-through date. If billing management is unavailable, [contact us to request cancellation](https://openwaves.world/contact/). Signing out or deleting a game account does not cancel a software subscription. Read the [Software License](https://openwaves.world/developers/license/) and [Hosted software access terms](https://openwaves.world/developers/license/hosted-access.html).

## Verify and install the package

The portal shows a SHA-256 checksum for each release. Replace `VERSION` in the commands below with the complete version from your downloaded filename, including its game/framework revision suffixes. On macOS or Linux, compare its checksum:

```sh
shasum -a 256 openwaves-water-VERSION.tgz
```

Use the filename you actually downloaded. If the checksum differs, discard that copy and download again before installing. Keep SDK packages and their source out of public repositories and public download directories.

In your own JavaScript application, place the tarball in a private `vendor` directory and install it with the tested Three.js peer:

```sh
npm install --save-exact ./vendor/openwaves-water-VERSION.tgz three@0.170.0
```

The package name is `@openwaves/water`; it is supplied as a licensed tarball, not a public npm download. Use your application's ES-module bundler and a WebGL2 browser. Keep your lockfile. TypeScript projects also need compatible `@types/three` declarations.

To inspect the supplied example without changing your application, extract the tarball into a new private directory, serve its `package` folder, and open the example:

```sh
mkdir openwaves-preview
cd openwaves-preview
tar -xzf ../openwaves-water-VERSION.tgz
python3 -m http.server 8098 --directory package
```

Open [the full environmental example](http://localhost:8098/examples/). It starts with the original Reef Pass bathymetry, water, sky and weather, and no objects on the water. Its clear, partly cloudy, overcast, rain and storm presets use the original weather module. The optional local GLB chooser adds your own passive floating models. No Google Maps or forecast request happens until you press a live button.

The example loads Three.js and OrbitControls from jsDelivr, and optional tile-renderer modules from esm.sh, requiring an internet connection. A production application can bundle its installed dependencies locally.

## Environmental source coverage

| Part of the production environment | Original implementation included | Integration boundary |
| --- | --- | --- |
| Barrel and wave motion | `crests`, `crest-propagation`, `wavefield`, `bore-flow`, `breaker-patch`; original shoaling, refraction, wind-dependent breaking, ballistic lip/jet, barrel thickness and collapse GLSL | Same shore-oriented solver. The CPU contact field is single-valued; this is not a volumetric fluid solver. |
| Water appearance | `water` vertex/fragment shaders, procedural `textures`, `photo-floor` water shader helper | Original Fresnel, sky reflection, refraction/absorption, foam, depth and underwater shading. Independent shore input is needed for a mapped coast. |
| Current production surface look | `overlay-look`, `surface-appearance`, `lip-touchdown` | Original default foam relief, crash bands, wake-only foam, microdetail and touchdown spray, with the original update order. |
| Foam and spray | `foam`, `foam-placement`, `breakinglip`, `breaking-foam`, `impact-spray`, `crest-spill`, `spray` | Original emitters; on-object contact triggers belong to the application's objects. |
| Reflections and caustics | `object-reflections`, `refracted-caustics`, `render-texture-pass` | Application supplies reflection objects; no game models are bundled. |
| Sky, light and weather | `sky`, `sun-path`, `sky-environment`, `weather`, original `Wind` | Original time, clouds, rain, lightning, light/fog and gusts. Weather audio uses a caller-unlocked AudioContext. Forecast sources are free and need no account; keep their credits visible. |
| Terrain and depth | `bathymetry`, `reef-layout`, original `terrain` material and procedural texture generators | Original Reef Pass defaults; other locations need independently licensed depth, shore and imagery. |
| Object interaction with water | `surface-ripples`, `hull-displacement`, `hull-wake-history`, `impact-waves`, `wake-sampling`, `rideable-wake`, `surface-probe`, `visual-contacts`, `wake` | Original hull wave/impact fields and optional GPU sampling of the SDK water. Application supplies object pose/velocity/footprint. |
| Contact presentation helpers | `carve-spray`, `hull-contact`, `hull-impact`, `hull-water-feedback`, `water-hull` | Original reusable calculations; no steering, propulsion, craft forces, equipment definitions or player controls. |

The release's `dist/build-manifest.json` lists its exact source modules and optional feature availability. Older game commits may predate newer modules; their export is `null` and `water.features` reports availability. A current-production release uses its actual `OverlayLook`; it does not silently substitute classic water if that initialization fails. Compare the pinned game commit, rather than assuming all historical packages have the same features.

The deployed game's texture loader normally loads lossless PNGs baked from its deterministic `buildTextures` generator, with the same dimensions, sampling and color-space settings, and falls back to that generator when the cache is unavailable. The SDK runs the original generator directly; it does not ship the game's texture cache or change the texture algorithm. This source audit is not a pixel-by-pixel comparison of the deployed PNG files.

## Integrate the full environment

This module assumes your application owns a Three.js scene, PerspectiveCamera, renderer and animation loop. Keep the world frame in metres, +Y up, with still water at Y=0. Keep environmental group transforms at identity.

```js
import * as THREE from 'three';
import { OceanSimulation } from '@openwaves/water';
import { FullWaterRenderer, ENVIRONMENT_DEFAULTS } from '@openwaves/water/full-water';

// These are the game's output settings; the wrapper doesn't replace host settings.
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.85;
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
camera.fov = ENVIRONMENT_DEFAULTS.camera.fov;
camera.near = ENVIRONMENT_DEFAULTS.camera.near;
camera.far = ENVIRONMENT_DEFAULTS.camera.far;
camera.updateProjectionMatrix();

const ocean = new OceanSimulation(); // Original Reef Pass depth and wave/wind defaults.
const water = new FullWaterRenderer(renderer, scene, ocean, { quality: 'medium' });
const focus = new THREE.Vector3(0, 1, -345);
camera.position.set(45, 7, -440);
camera.lookAt(focus);

// Match the game's 230-second preparation duration with the SDK's 30 Hz solver.
// Yield to keep the loading UI responsive; foam develops during rendered frames.
for (let i = 0; i < 230; i++) {
  ocean.advance(1);
  if (i % 8 === 0) await new Promise(requestAnimationFrame);
}

export function frame(deltaSeconds) {
  const dt = Math.min(0.1, Math.max(0, deltaSeconds));
  ocean.advance(dt);
  // Update any application-owned floating objects here.
  water.update(dt, camera, { focus });
  // maps.update(); // If an optional Google overlay has been created.
  renderer.render(scene, camera);
}
export function dispose() {
  water.dispose();
  ocean.dispose();
}
```

`FullWaterRenderer.update` renders its offscreen effects but does not advance physics or call the host's final scene render. It restores renderer state after reflection passes. The renderer owns the resources it creates, including its sky/lights, meshes, effects and render targets. The simulation owns its default bathymetry texture and crest textures. Dispose the renderer before the simulation. Your scene, camera, WebGLRenderer, supplied textures and externally created `GameBathymetry` remain yours.

`quality` accepts `low`, `medium` or `high`, using original configuration values for water grid, foam, particles, shadows and seabed spacing. The full renderer performs several GPU passes; the example uses medium quality. When creating a renderer, the game uses these constructor hints and initial pixel budget:

```js
const renderer = new THREE.WebGLRenderer({
  antialias: true, powerPreference: 'high-performance', alpha: false,
});
const q = ENVIRONMENT_DEFAULTS.quality.medium;
renderer.setPixelRatio(Math.max(0.5, Math.min(
  devicePixelRatio, q.pixelRatio, Math.sqrt(q.maxPixels / (width * height)),
)));
renderer.setSize(width, height);
```

Recalculate the pixel budget on resize. The game also adapts a runtime resolution scale to performance; that application policy remains caller-owned, and the example starts at scale 1.

## Environmental and contact controls

- `water.setWeather('storm')` uses original weather presets: `clear`, `partly-cloudy`, `overcast`, `rain`, `storm`. Pass `{instant:true}` for immediate application. Missing or unsupported conditions return `false` and preserve the previous setting. Snow and fog-only forecasts are reported as unsupported rather than substituted.
- `water.setTime(hours)` uses the original sky's 0–24 hour calculation. `water.setLocation({latitude,longitude,bearing})` supplies the geographic frame to that calculation; pass `null` to return to the default procedural world.
- `water.reflectionObjects.push(yourObject)` includes application-owned objects in the original reflection pass. The wrapper temporarily uses the original reflection layer and restores object/light layer masks. Avoid assigning other host objects to the game's reserved reflection layer 5.
- `water.addRipple(x,z,strength)` emits an original contact ripple. `water.splatFoam(x,z,radius,intensity)` writes into the original foam pass. `water.disturbWater(yourObject,strength,interval)` samples contact from your object through the original contact helper.
- `const wake = water.createWake({width:0.5,strength:0.3})` creates an original foam wake. Call `wake.push(x,z)` as your object moves, `wake.breakTrail()` across teleports and `wake.dispose()` when removed. The renderer updates its shader and time.

For the newer production hull-wave system, call `water.registerHull({object,velocity,length,width,height,draft,massKg})`. Pose is read from the object's world transform, velocity from your live vector, and the dimensions are metres. It returns `{owner,dispose}`; the wrapper passes these inputs into the original water-effect modules without applying forces. `water.createWaterSampler({owner})` supplies `height` and `sample` with historical wakes while excluding that owner's own wake, using the original `RideableWake`. The imported-model example uses this path when available. `water.addImpact({x,z,height,halfLength,halfBeam,fx,fz,vx,vz})` submits an original landing wave packet at the current time.

For the original small-footprint drawn-surface coupling, create a sampler with `{owner,drawnSurface:true}` and call `water.updateDrawnSurface(x,z,{driftX,driftZ})` after `water.update`, near the object being sampled. It asynchronously reads only the SDK's own water shader, never Google pixels or geometry. The original freshness/edge blending applies; absent GPU readings retain the original physics sample. A probe covers one small local patch, not every object or the whole ocean. The underlying original `CarveSprayMotion`, `HullImpactMotion`, `hullContactPoint`, `hullContactBudget`, `hullImpactResponse` and `hullImpactSpray` exports are available for advanced applications already providing the required motion/contact state.

To enable the original weather's rain and thunder, unlock your own `AudioContext` in a user gesture, then call `water.setAudio({ctx:audioContext,master:yourGainNode,enabled:true})`. The wrapper tracks and releases only the nodes the original weather code creates. `water.setAudio(null)` detaches them; disposal also detaches them. Your app retains ownership of the context and master gain. No autoplay or context creation occurs inside the SDK.

No floating models are bundled. The example accepts self-contained `.glb` files, up to 32 MiB each and 16 simultaneous objects. Models stay local when selected or dropped. It uses the SDK's passive `FloatingAsset`: sampled waves drive bobbing, tilt and drag, with configurable scale, origin, draft, mass and damping. This is not the game's craft/foiling physics or a vehicle controller.

For a reusable asset folder, place your own GLBs in `examples/environment/assets/`, run `node tools/asset-manifest.mjs` from the extracted package root, then refresh the example. `--watch` refreshes the manifest after file changes. Optional `boat.float.json` files beside `boat.glb` specify presets; see `examples/environment/assets/README.txt`. Embedded, uncompressed GLB exports are supported; external resources and Draco/KTX2 decoder setup are not included.

## Physics and bathymetry contract

- Settings use metres and seconds. Direction settings are degrees toward travel: 0 is +Z and 90 is +X. Returned `slopeDir` is radians. The default ocean extends offshore into negative Z.
- This is the original shore-oriented domain: crests spawn near z=-1250 and retire near z=60. Swell direction accepts -80 to +80 degrees. It is not an arbitrary globe-wide ocean solver.
- `advance(dt)` accepts 0–1 seconds and advances at a fixed 30 Hz. Fractional time carries forward. The application owns pause/catch-up policy.
- `setSwell({amplitude,period,direction})` changes future waves; amplitude accepts 0–10 m and period 1–60 s. `setWind(speed,direction)` accepts 0–50 m/s. The original Wind field supplies visual gusts.
- `sample(x,z,{out,normal,submergedY})` returns height, normal, velocity/acceleration, breaking/foam and crest information. Reuse `out` to avoid allocation; `normal:false` omits extra normal samples. `submergedY` is relative to still water.
- `height(x,z)` is a continuous single-valued contact surface. GPU breaker displacement can form more complex visual profiles; CPU and GPU geometry are not asserted to be bit-identical.
- `setFocus(x,z)` selects the view-centered crest set. One environmental view per simulation is supported. `getRenderState()` returns borrowed textures/uniform data; never dispose these separately.

A custom `bathymetry.depth(x,z)` must return positive finite depth below still water. The solver receives these values unchanged. Keep depth callbacks synchronous and inexpensive. For custom renderer geometry, pass an independent `elevation(x,z)` and optional `reefMask(x,z)`; otherwise elevation defaults to negative depth. The default original bathymetry uses its original analytic elevation and reef mask.

`createBathymetryGrid` in the environment entry point accepts independent elevation or positive-down depth grids, interpolates bilinearly and records source/license/datum metadata. Its own explicit `minDepth` setting defaults to 0.1 m, which is distinct from the solver's unchanged-depth contract and is not a coastline mask. Missing/outside samples throw unless you explicitly select a missing-depth or clamping policy. Depth data must cover the simulated domain. Google tiles are not sampled for depth.

## Forecast integration

`@openwaves/water/environment` normalizes units/timestamps, converts compass directions to the Google adapter's local frame, and applies wind/swell through the public solver API. Connect normalized weather directly to the original renderer:

```js
import { applyEnvironment } from '@openwaves/water/environment';
function acceptForecast(snapshot) {
  const applied = applyEnvironment(ocean, snapshot, {
    onWeather: weather => {
      if (!water.setWeather(weather)) {
        // Report that this weather type is unavailable; existing weather remains.
      }
    },
  });
  // Display applied.issues and snapshot.attributions in your application.
}
```

Fetch conditions on your own server with `createForecastProvider`. Every source it uses is free, needs no account or key, and allows commercial use with credit:

| Data | Source | Terms |
| --- | --- | --- |
| Weather and wind, worldwide | [MET Norway Locationforecast](https://api.met.no/) | CC BY 4.0 / NLOD 2.0. Credit "Data from MET Norway". |
| Waves, servers (first choice) | [NOAA NCEP GFS-Wave](https://nomads.ncep.noaa.gov/) (WAVEWATCH III, 0.25°) through NOAA's GRIB filter | US public domain. |
| Waves, browsers and server backup | [PacIOOS WAVEWATCH III global model](https://www.pacioos.hawaii.edu/waves/model-global/) (University of Hawaii with NOAA, 0.5°) | "May be used and redistributed for free"; no warranty. |

```js
import { createForecastProvider } from '@openwaves/water/environment';
const forecast = createForecastProvider({
  userAgent: 'YourApp/1.0 https://your-site.example/contact',  // required by MET Norway
});
const snapshot = await forecast.fetch({ latitude: 32.8, longitude: -117.27, bearing: 0 });
// Cache the snapshot on your server and send it to your scene.
```

Their fair-use rules are the application's to keep: MET Norway asks every request to identify the application (the `userAgent`, with a contact URL or email), allows at most 20 requests per second per application in total, and asks browsers to go through the application's server; NOAA limits each IP address to 120 requests a minute. One snapshot per location per hour is plenty, because the models update every few hours. In a browser the provider sends no User-Agent (browsers cannot) and uses PacIOOS only, which suits low-volume testing such as the included example.

`waveMode` defaults to `'auto'`: the nearest wave-model cell with data, using its swell where the model has swell and otherwise the combined wind-wave and swell height (the snapshot's issues say which). `'swell'` and `'total-waves'` are strict. `snapshot.swell.modelCell` reports the cell used and its distance; coastal points read the nearest offshore cell, so sheltered water can be calmer than the forecast. On a server the NOAA model also covers the Mediterranean and Baltic; the Black Sea, Caspian Sea and Great Lakes have no wave data in these models, and the snapshot says so instead of inventing any. Wrong units, stale or missing values stay unavailable with explanatory `issues`; default freshness is two hours. Requests do not create polling. `applyEnvironment` approximates amplitude as half significant wave height, reports bounds, and skips outgoing swell outside the solver's shoreward domain.

`normalizeEnvironment` accepts an already-fetched MET Norway Locationforecast 2.0 response (`weather`) and a wave table (`marine`, in ERDDAP JSON layout); `decodeWaveGrib` turns simple-packed GRIB2 wave fields from NOAA's filter into that table. This is model forecast adaptation for a simulation, not a beach measurement or navigation service. The SDK provides no forecast proxy, cache or polling. Keep source credits (`snapshot.attributions`) visible and identify data adapted for simulation.

## Google Maps and independent shore data

Install `3d-tiles-renderer@0.5.3` separately and supply its constructors. Google account eligibility, billing and terms are separate from your Open Waves license.

```js
import { TilesRenderer } from '3d-tiles-renderer/three';
import { ReorientationPlugin } from '3d-tiles-renderer/three/plugins';
import { GoogleCloudAuthPlugin } from '3d-tiles-renderer/core/plugins';
import { createGoogleTilesOverlay } from '@openwaves/water/google-tiles';

const maps = createGoogleTilesOverlay({
  TilesRenderer, ReorientationPlugin, GoogleCloudAuthPlugin,
  scene, camera, renderer,
  latitude: YOUR_LATITUDE, longitude: YOUR_LONGITUDE, bearing: YOUR_BEARING,
  height: YOUR_INDEPENDENT_ELLIPSOID_HEIGHT, waterLevel: 0,
  apiKey: YOUR_RESTRICTED_BROWSER_KEY,
  attributionContainer: YOUR_VISIBLE_CREDIT_ELEMENT,
  googleMapsLogoURL: YOUR_COMPLIANT_GOOGLE_MAPS_LOGO_URL,
});
// Each frame after camera movement: maps.update().
// After resize: maps.resize(). On removal: maps.dispose().
```

Before bearing rotation, local +X points west and +Z north. `bearing` rotates this frame around +Y; it is not a compass heading. Origin `height` is WGS84 ellipsoid altitude, placed at `offset`, default `(0,0,0)`. Choose an independent datum conversion so the desired still-water plane is local Y=0; the original full renderer does not support a nonzero water-level transform. The Google adapter's `waterLevel` return value does not move water. Local Reef Pass bathymetry does not become the selected geographic location's depth model.

Keep Google Maps branding and attribution for every visible tile visible in the map viewport, including fullscreen. See [Map Tiles API policies](https://developers.google.com/maps/documentation/tile/policies) and [EEA integration guidance](https://developers.google.com/maps/comms/eea/map-tiles). The adapter does not extract tile colors/depth, derive collision/shore geometry, change tile materials or geometry, persist content, or provide a shared API key.

The original water shoreline shader helper remains bundled. `water.setShoreTexture(texture,{source:{name,license}})` accepts independently licensed original-format shoreline data: 1024×1024 RGBA float data; R is signed shore distance in metres, G bed elevation, B the still-water mask, over x[-4000,4000], z[-4350,3650]. The texture stays caller-owned; `null` disables it. This does not generate data, authorize Google-derived input, or automatically reconstruct the game's photographic mapped floor.

`water.setLandTexture(texture,{source:{name,license}})` enables the original terrain's broad imagery-color blend using your independent image. Its original fixed projection covers x[-1200,1200], z[-1700,500], with U reversed for the legacy east-positive image layout. The caller retains the texture and must prepare those coordinates; `null` disables it. Neither input changes Google's materials or authorizes reuse of Google-derived pixels.

## Integration boundaries and distribution

The package preserves original environmental modules and relevant configuration from its recorded game commit. The wrapper isolates game debug globals/query flags and extracts the original seabed material and water photo-floor shader helper without importing provider extraction code. The procedural scene retains original sky, weather, textures, terrain functions and water effects. The example starts without on-water assets.

Mapped-site data generation, Google-derived photographic floor/shore inputs, game controls/camera rigs, equipment, multiplayer, account services and game UI are outside this SDK. Weather audio is not enabled automatically. Applications supply their own licensed map/depth/image data and integration lifecycle. Overall scene parity depends on those inputs and renderer/camera settings; it is not inferred solely from shader identity. Interactive environmental behavior is not a validated engineering, forecasting or navigation model.

`WaterSurface` and `EnvironmentVisuals` remain optional lower-level compatibility exports. They are not the full game renderer and are not used by the main example. New integrations should start with `FullWaterRenderer`.

See the `LICENSE` and `THIRD_PARTY_NOTICES.txt` files in your downloaded package for your grant and retained upstream notices. Releases include only allowlisted implementation, declarations, examples and notices; no bundled on-water models, Google content, keys or customer records. Keep the tarball/source private. `private:true` prevents accidental public npm publication while allowing local tarball installation.

## Update, pin or revert

1. Sign in and review the releases your license can download. The server checks your entitlement; public release metadata alone does not grant access.
2. Download an entitled release during your paid term, verify its checksum and save its license and certificate with your private backup.
3. Read that release's included documentation and changes. Test in a branch or staging copy of your application before replacing production code.
4. Install the exact new tarball and compatible Three.js peer; commit your application's lockfile to its private repository. Keep your known-working package until you finish testing.
5. If needed, reinstall your saved prior tarball and restore the matching lockfile and integration changes from your application's version control.

A canceled subscription can retain access through its recorded paid term. When that term ends, the rights to use your last lawfully downloaded version continue, while new update access ends. Downloading or keeping a package is not permission to share the standalone SDK publicly. A certificate is a record of the license and acquired versions, not a sublicensing right.

## Troubleshooting

- **No sign-in code:** confirm the checkout email, check spam, request a fresh code and use only the newest one. Repeated requests can be rate-limited. An address without a software purchase receives the same generic on-screen response.
- **A paid order is missing:** refresh after payment confirmation. If it remains missing, contact us with the receipt reference and purchaser email. Do not send payment details or pay again just to retry delivery.
- **A download fails:** refresh your software account, sign in again if needed and retry. If a purchased release remains unavailable, contact us for delivery help.
- **A download checksum differs:** discard that copy and download it again before installing.
- **The example is blank:** use an HTTP server, a WebGL2 browser and the exact Three.js peer. Check the browser console for module/network or shader errors. An internet connection is needed for the example's CDN imports.
- **The water is flat initially:** waves start offshore; pre-roll the simulation or wait for propagation. Confirm that both `ocean.advance` and `water.update` run and that your depth callback returns positive finite values.
- **A buoy does not match the water:** use the same local X/Z frame as the simulation and keep still water at Y=0 for `FullWaterRenderer`. Use `FloatingAsset` for a passive model, and check its scale, origin and waterline preset. Do not mix ellipsoid altitude with local water depth.
- **Google scenery fails:** verify your own account eligibility, enabled service, key restrictions, billing and provider response. Try the procedural example to isolate the water integration. Google service access is separate from the SDK purchase.

For delivery, license corrections, cancellation or privacy requests, [contact Open Waves](https://openwaves.world/contact/). Include your purchaser email, release version, the relevant error and a concise description. Ongoing support and custom integration are included only when stated on your order.
