threlte logo
@threlte/extras

<Float>

webgl <Float> works with WebGL and is available from the @threlte/extras export. webgpu <Float> works with WebGPU and is available from the @threlte/extras/webgpu export.

This component is a port of drei’s <Float> component and makes its contents float or hover.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'
</script>

<div>
  <Canvas>
    <Scene />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import type { Snippet } from 'svelte'
  import { Float } from '@threlte/extras'
  import { Spring } from 'svelte/motion'

  interface Props {
    children?: Snippet<[{ hovering: boolean }]>
  }

  let { children }: Props = $props()

  const scale = new Spring(1)

  let hovering = $state(false)

  const onPointerEnter = () => {
    hovering = true
    scale.set(1.1)
  }

  const onPointerLeave = () => {
    hovering = false
    scale.set(1)
  }
</script>

<Float
  floatIntensity={5}
  scale={scale.current}
  rotationIntensity={2}
  rotationSpeed={[1, 0.5, 0.2]}
  onpointerenter={onPointerEnter}
  onpointerleave={onPointerLeave}
>
  {@render children?.({ hovering })}
</Float>
<script lang="ts">
  import Blob from './Blob.svelte'
  import type { Mesh } from 'three'
  import { Environment, Float, Grid, interactivity, useGltf, useDraco } from '@threlte/extras'
  import { T } from '@threlte/core'

  type Nodes = `ball-${'1' | '2' | '3' | '4' | '5'}`

  interactivity()

  const dracoLoader = useDraco()
  const gltf = useGltf<{
    nodes: Record<Nodes, Mesh>
    materials: {}
  }>('/models/blobs/blobs.glb', {
    dracoLoader
  })

  const red = '#fe3d00'
  const blue = '#0000ff'
</script>

<Environment url="/textures/equirectangular/hdr/shanghai_riverside_1k.hdr" />

<Float
  rotationIntensity={0.15}
  rotationSpeed={2}
>
  <T.PerspectiveCamera
    makeDefault
    position.y={10}
    position.z={10}
    fov={90}
    oncreate={(ref) => {
      ref.lookAt(0, 0, 0)
    }}
  />
</Float>

<Grid
  position.y={-10}
  sectionThickness={1}
  infiniteGrid
  cellColor="#dddddd"
  sectionColor="#ffffff"
  sectionSize={10}
  cellSize={2}
/>

{#await gltf then { nodes }}
  {#each Object.values(nodes) as node}
    <Blob>
      {#snippet children({ hovering })}
        <T.Mesh>
          <T.MeshPhysicalMaterial
            reflectivity={1}
            metalness={0.9}
            roughness={0.2}
            color={hovering ? red : blue}
          />
          {#if node.geometry}
            <T is={node.geometry} />
          {/if}
        </T.Mesh>
      {/snippet}
    </Blob>
  {/each}
{/await}

Examples

Basic Example

FloatingMesh.svelte
<script lang="ts">
  import { T } from '@threlte/core'
  import { Float } from '@threlte/extras'
</script>

<Float
  floatIntensity={5}
  scale={$scale}
  rotationIntensity={2}
>
  <T.Mesh>
    <T.MeshStandardMaterial color={'orange'} />
    <T.BoxGeometry args={[5, 5, 5]} />
  </T.Mesh>
</Float>

Floating

floatingRange determines the allowed range of position trasformation, and by extension the direction in which the children will move. If you provide a single number tuple, like [-0.1,0.1] the movement will happen on y axis. Alternatively you can provide an array of three tuples to change the movement axes. For example [[0,0],[0,0],[-0.5,0.5]] will move children between -0.5 and 0.5 relative to the starting position on the Z axis.

Rotation

Rotation is set by rotationSpeed and rotationIntensity. Both of them need to be different from 0 to enable rotation. Providing a number in either of them applies it to all three axes. You get more granual control by passing an array [x: number, y: number, z: number]. rotationSpeed is responsible for the speed of the animation and rotationIntensity for the angle.

seed is responsible for the starting state of the animations and is random by default. Setting to a fixed value to get a predictable starting result.

Component Signature

<Float> extends < T . Group > and supports all its props, snippets, bindings and events.

Props

name
type
required
default
description

floatingRange
[number, number] | [x: [number, number], y: [number, number], z: [number, number]]
no
[-0.1, 0.1]
The minimum and maximum offset, in world units. A single pair floats along the y-axis only, and three pairs set the range of the x, y and z axes.

floatIntensity
number | [x: number, y: number, z: number]
no
1
Multiplies how far the children move. A tuple sets it per axis.

rotationIntensity
number | [x: number, y: number, z: number]
no
0
How far the children rock back and forth. 0 disables rotation. A tuple sets it per axis.

rotationSpeed
number | [x: number, y: number, z: number]
no
0
How fast the children rock back and forth. A tuple sets it per axis.

seed
number
no
Math.random() * 10000
The starting point of the animation, so several floating objects don't move in sync.

speed
number | [x: number, y: number, z: number]
no
1
How fast the children bob up and down. A tuple sets the speed per axis.