Displays multiples instances with different layouts (flex, sticky, fixed, etc.).

Sticky position
Nested flex layouts
Custom WebGL Renderer
Fixed position
100% Map style by Stamen Design, © OpenStreetMap contributors
import { WebGLRenderer } from 'three';
import { MapControls } from 'three/examples/jsm/controls/MapControls.js';

import StadiaMaps from 'ol/source/StadiaMaps.js';

import Extent from '@giro3d/giro3d/core/geographic/Extent.js';
import Instance from '@giro3d/giro3d/core/Instance.js';
import ColorLayer from '@giro3d/giro3d/core/layer/ColorLayer.js';
import Map from '@giro3d/giro3d/entities/Map.js';
import TiledImageSource from '@giro3d/giro3d/sources/TiledImageSource.js';

const extent = new Extent(

const source = new TiledImageSource({
    source: new StadiaMaps({ layer: 'stamen_watercolor', wrapX: false }),

function buildViewer(viewerDiv, defaultRenderer = true) {
    const renderer = { clearColor: false };
    if (!defaultRenderer) {
        renderer.renderer = new WebGLRenderer({ antialias: true, alpha: true });
    const instance = new Instance(viewerDiv, { renderer, crs: extent.crs() });
    // Creates a map that will contain the layer
    const map = new Map('planar', { extent, maxSubdivisionLevel: 10 });


    // Adds an TMS imagery layer
        new ColorLayer({
            name: 'osm',
    ).catch(e => console.error(e));

    instance.camera.camera3D.position.set(0, 0, 25000000);

    const controls = new MapControls(instance.camera.camera3D, instance.domElement);


    // Disable zoom so it doesn't capture scrolling
    controls.enableZoom = false;

// Remove the pre-generated default HTML elements for this example

// Dynamically find all viewers we have to build
const viewerDivs = document.getElementsByClassName('viewer');
for (let i = 0; i < viewerDivs.length; i += 1) {

// Dynamically find all viewers we have to build with custom WebGLRenderers
const viewerCustomRendererDivs = document.getElementsByClassName('viewer-custom-renderer');
for (let i = 0; i < viewerCustomRendererDivs.length; i += 1) {
    buildViewer(viewerCustomRendererDivs[i], false);
<!doctype html>
<html lang="en">
        <title>Multiple instances in different layouts</title>
        <meta charset="UTF-8" />
        <meta name="name" content="layouts" />
        <meta name="description" content="Displays multiples instances with different layouts (flex, sticky, fixed, etc.)." />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />

        <link rel="icon" href="https://giro3d.org/images/favicon.svg" />
        <link href="https://giro3d.org/assets/bootstrap-custom.css" rel="stylesheet" />
        <script src="https://giro3d.org/assets/bootstrap.bundle.min.js"></script>

        /* Reset page height so we can scroll */
        html {
          height: auto;
        body {
          height: auto;

        #viewerDivSticky {
          background: linear-gradient(to right, rgb(125, 199, 144), rgb(11, 59, 23));

        #legendNestedFlex {
          writing-mode: vertical-rl;
        #containerFlex {
          background: linear-gradient(to right, rgb(251, 165, 116), rgb(216, 245, 251));

        #viewerDivOverflow {
          background: linear-gradient(to right, rgb(251, 251, 255), rgb(215, 223, 252));

        #viewerDivFixed {
          background: linear-gradient(to right, rgb(65, 80, 95), rgb(36, 37, 38));
        #legendFixed {
          color: #fff;

        #source {
          margin-bottom: 250px !important;

        <div id="viewerDiv" class="m-0 p-0 w-100 h-100"></div>
        <div id="panelDiv" class="position-absolute top-0 start-0 mh-100 overflow-auto"></div>

            In all examples we are using Bootstrap classes for styling.
            Here, we are using:
            - plain-old styles to show how to use different layouts,
            - Bootstrap classes and embedded <style> for pure styling (borders, padding, margin, backgrounds, etc.).

        <div id="legendSticky" style="position: sticky; z-index: 1021; top: 0" class="ps-2">
            Sticky position
        <!-- A container must have a size (height+width), and can have any style applied-->
            style="position: sticky; z-index: 1020; top: 0; height: 300px; width: 100%"
            class="viewer p-2 border-bottom border-5 border-dark"

        <div id="containerFlex" style="display: flex; align-items: stretch; height: 50vh" class="mt-5">
            <div id="legendNestedFlex" class="pt-2">Nested flex layouts</div>
            <!-- In flex display, a container can have a flex property instead of a size -->
            <div id="viewerDivNestedFlex1" style="flex: 1 1 auto" class="viewer ps-2 pe-2"></div>

                In case you are using your own renderer, you are responsible for the layout, for instance:
                - a parent div to set the element size - this is were you would want to apply borders, padding, backgrounds, etc.
                - a viewport div that fills it parent and has "position: relative" and "overflow: hidden"
                    - this is to ensure a correct sizing of the renderer when the parent div is resized
                - the div that will hold your renderer, that fills its parent
            <div style="min-width: 20vw; height: 100%" class="p-5 border border-3 border-dark">
                    style="position: relative; overflow: hidden; width: 100%; height: 100%"
                        style="width: 100%; height: 100%"
                <span class="position-absolute">Custom WebGL Renderer</span>

        <div id="legendOverflow" class="mt-5 ps-2">Overflow</div>
        <!-- You can also have containers that are larger than the viewport -->
        <div id="viewerDivOverflow" style="width: 100%; height: 200vh" class="viewer p-2"></div>

        <!-- You can also have containers that are at fixed positions -->
            style="position: fixed; left: 0; bottom: 0; height: 200px; width: 50vw"
            class="viewer p-2 border border-3 border-dark"
        <!-- Any Giro3D container can have overlays, as long as they are after in the DOM (or using z-index) -->
        <span id="legendFixed" style="position: fixed; left: 0; bottom: 0" class="ps-2">
            Fixed position

        <script type="module" src="index.js"></script>
            /* activate popovers */
            const popoverTriggerList = [].slice.call(
                // bootstrap is used as script in the template, disable warning about undef
                // eslint-disable-next-line no-undef
                popoverTriggerEl =>
                    new bootstrap.Popover(popoverTriggerEl, {
                        trigger: 'hover',
                        placement: 'left',
                        content: document.getElementById(
                        html: true,
    "name": "layouts",
    "dependencies": {
        "@giro3d/giro3d": "0.37.2"
    "devDependencies": {
        "vite": "^3.2.3"
    "scripts": {
        "start": "vite",
        "build": "vite build"