LAB_07 // THE_OASIS

THE
OASIS

Spline // Selbst gehostete Szene // Ziehen zum Drehen

Ein kubischer Ozean zum Runterkommen: ein Segelboot auf einer Wasseroberfläche mit Kaustiken, darunter Kelp und Fische, darüber Wolken. In Spline modelliert, von der Spline-Runtime gerendert, und die ganze Szene kommt von dieser Domain, keine Calls nach Hause.

01

WATER_CUBE

ZIEHEN ZUM DREHEN // DRÜCK DAS SEGELBOOT RUHIG RUNTER, VERSENKEN KANNST DU ES NICHT

So ist es gebaut [+]
<template>
  <div class="oasis-wrap">
    <canvas ref="canvasRef" class="oasis-canvas"></canvas>
    <p v-if="loading" class="oasis-loading font-mono" role="status">LOADING_SCENE<span class="oasis-caret">_</span></p>
  </div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue"
// @ts-ignore — runtime ships its own types but vite resolves the browser build
import { Application } from "@splinetool/runtime"

// Spline water-cube scene, self-hosted from /oasis/scene.splinecode so no
// request ever leaves this domain. The runtime drives its own render loop.
const props = withDefaults(defineProps<{ scene?: string }>(), {
  scene: "/oasis/scene.splinecode",
})

const canvasRef = ref<HTMLCanvasElement | null>(null)
const loading = ref(true)
let app: InstanceType<typeof Application> | null = null

onMounted(async () => {
  // wasmPath keeps the runtime's wasm on this domain too (default is unpkg)
  app = new Application(canvasRef.value!, { wasmPath: "/oasis/wasm" })
  try {
    await app.load(props.scene)
  } finally {
    loading.value = false
  }
})

onBeforeUnmount(() => {
  app?.dispose()
  app = null
})
</script>

<style scoped>
.oasis-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.oasis-canvas {
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
}
.oasis-loading {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.24em;
  color: rgba(27, 27, 27, 0.5);
  pointer-events: none;
}
.oasis-caret { animation: oasis-blink 1s steps(1) infinite; }
@keyframes oasis-blink { 50% { opacity: 0; } }
</style>

Die Szene liegt unter /oasis/scene.splinecode (310 KB, selbst gehostet). Die npm-Runtime fährt ihren eigenen Render-Loop auf einem Canvas, die Vue-Seite ist ein Mount, ein Load und ein Dispose.