This example uses OpenLayers v10.9.0. The latest is v.
Edit

#### Rendering 16-bit NumpyTiles

numpytiles 1 webgl 18

#####

##### Contrast stretch

- Min
- Max

Renders a multi-byte depth source image directly using WebGL.

This example uses a `ol/source/DataTile` source to load multi-byte raster data in the NumpyTile format. The source is rendered with a `ol/layer/WebGLTile` layer. Adjusting the sliders above performs a contrast stretch by adjusting the style variables set on the layer.

##### main.js

    import NumpyLoader from '@planet/ol-numpytiles';
    import Map from 'ol/Map.js';
    import View from 'ol/View.js';
    import TileLayer from 'ol/layer/WebGLTile.js';
    import {fromLonLat} from 'ol/proj.js';
    import DataTileSource from 'ol/source/DataTile.js';

    // 16-bit COG
    // Which will be served as NumpyTiles.
    const COG =
      'https://storage.googleapis.com/open-cogs/stac-examples/20201211_223832_CS2_analytic.tif';

    function numpyTileLoader(z, x, y) {
      const url = `https://titiler.xyz/cog/tiles/WebMercatorQuad/${z}/${x}/${y}?format=npy&url=${encodeURIComponent(
        COG,
      )}`;

      return fetch(url)
        .then((r) => r.arrayBuffer())
        .then((buffer) => NumpyLoader.fromArrayBuffer(buffer))
        .then((numpyData) => {
          // flatten the numpy data
          const dataTile = new Float32Array(256 * 256 * 5);
          const bandSize = 256 * 256;
          for (let x = 0; x < 256; x++) {
            for (let y = 0; y < 256; y++) {
              const px = x + y * 256;
              dataTile[px * 5 + 0] = numpyData.data[y * 256 + x];
              dataTile[px * 5 + 1] = numpyData.data[bandSize + y * 256 + x];
              dataTile[px * 5 + 2] = numpyData.data[bandSize * 2 + y * 256 + x];
              dataTile[px * 5 + 3] = numpyData.data[bandSize * 3 + y * 256 + x];
              dataTile[px * 5 + 4] =
                numpyData.data[bandSize * 4 + y * 256 + x] > 0 ? 1.0 : 0;
            }
          }
          return dataTile;
        });
    }

    const interpolateBand = (bandIdx) => [
      'interpolate',
      ['linear'],
      ['band', bandIdx],
      ['var', 'bMin'],
      0,
      ['var', 'bMax'],
      1,
    ];

    const initialMin = 3000;
    const initialMax = 18000;

    const numpyLayer = new TileLayer({
      style: {
        color: [
          'array',
          interpolateBand(3),
          interpolateBand(2),
          interpolateBand(1),
          ['band', 5],
        ],
        variables: {
          'bMin': initialMin,
          'bMax': initialMax,
        },
      },
      source: new DataTileSource({
        loader: numpyTileLoader,
        bandCount: 5,
      }),
    });

    const map = new Map({
      target: 'map',
      layers: [numpyLayer],
      view: new View({
        center: fromLonLat([172.933, 1.3567]),
        zoom: 15,
      }),
    });

    const inputMin = document.getElementById('input-min');
    const inputMax = document.getElementById('input-max');
    const outputMin = document.getElementById('output-min');
    const outputMax = document.getElementById('output-max');

    inputMin.addEventListener('input', (evt) => {
      numpyLayer.updateStyleVariables({
        'bMin': parseFloat(evt.target.value),
        'bMax': parseFloat(inputMax.value),
      });
      outputMin.innerText = evt.target.value;
    });

    inputMax.addEventListener('input', (evt) => {
      numpyLayer.updateStyleVariables({
        'bMin': parseFloat(inputMin.value),
        'bMax': parseFloat(evt.target.value),
      });
      outputMax.innerText = evt.target.value;
    });

    inputMin.value = String(initialMin);
    inputMax.value = String(initialMax);
    outputMin.innerText = String(initialMin);
    outputMax.innerText = String(initialMax);

##### index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Rendering 16-bit NumpyTiles</title>
        <link rel="stylesheet" href="node_modules/ol/ol.css">
        <style>
          .map {
            width: 100%;
            height: 400px;
          }
          input[type="range"] {
            vertical-align: text-bottom;
          }
        </style>
      </head>
      <body>
        <div id="map" class="map"></div>

        <div>
          <h5>Contrast stretch</h5>
          <ul>
            <li>Min <input type="range" min="1000" max="10000" id="input-min"/> <span id="output-min"></span></li>
            <li>Max <input type="range" min="10000" max="50000" id="input-max"/> <span id="output-max"></span></li>
          </ul>
        </div>

        <script type="module" src="main.js"></script>
      </body>
    </html>

##### package.json

    {
      "name": "numpytile",
      "dependencies": {
        "ol": "10.9.0",
        "@planet/ol-numpytiles": "^3.0.1"
      },
      "devDependencies": {
        "vite": "^3.2.3"
      },
      "scripts": {
        "start": "vite",
        "build": "vite build"
      }
    }