---
title: useLoop
description: useLoop provides a convenient access to the render loop of the TresJS scene.
---
The `useLoop` composable allows you to register callbacks that run before and after each render cycle, or take complete control of the rendering process within `TresCanvas` components.
::dotted-diagram
::diagrams-render-loop
::
::
## Usage
::warning
`useLoop` can only be used in child components of a [`TresCanvas`](/api/components/tres-canvas) component, as its data is provided by [`TresCanvas`](/api/components/tres-canvas).
::
```ts
import { useLoop } from '@tresjs/core'
const { onBeforeRender, onRender } = useLoop()
onBeforeRender(() => {
console.log('before render')
})
onRender(() => {
console.log('after render')
})
```
### Priority
The `onBeforeRender` and `onRender` callbacks can be registered with a priority. The priority is a number that determines the order in which the callbacks are executed. The default priority is 0.
::code-group
```ts [onBeforeRender]
onBeforeRender(() => {
console.log('earlier before render')
}, -10)
onBeforeRender(() => {
console.log('just before render')
})
onBeforeRender(() => {
console.log('even closer before render')
}, 10)
```
```ts [onRender]
onRender(() => {
console.log('even closer after render')
}, -10)
onRender(() => {
console.log('just after render')
})
onRender(() => {
console.log('later after render')
}, 10)
```
::
### Register update callbacks
The most common use of `onBeforeRender` is to register update callbacks for animations, such as rotating or moving objects in the scene.
::code-group
```vue [AnimatedCube.vue]
```
```vue [App.vue]
```
::
### Take Over the Render Loop
You can take complete control of the rendering process by using the `render` method from `useLoop`. This allows you to implement custom rendering logic, post-processing effects, or conditional rendering.
```ts
import { useLoop, useTresContext } from '@tresjs/core'
const { render } = useLoop()
const { renderer, scene, camera } = useTresContext()
// Take over the render loop with custom logic
render((notifySuccess) => {
// Your custom rendering logic here
if (camera.activeCamera.value) {
renderer.instance.render(scene.value, camera.activeCamera.value)
// IMPORTANT: Call notifySuccess() to indicate the frame was rendered successfully
notifySuccess()
}
})
```
::warning
**Success Callback Required**: You must call the provided callback (named `notifySuccess()` in the example above) to properly notify the render loop that the frame was completed. This is essential for the render modes (`always`, `on-demand`, `manual`) to function correctly.
::
#### Custom Rendering Examples
Here are examples showing different custom rendering scenarios:
::code-group
```vue [Conditional Rendering]
```
```vue [Post-processing]
```
```vue [Multi-pass Rendering]
```
::
::warning
When you take over the render loop, you become responsible for:
- Manually triggering a render
- **Always calling `notifySuccess()` at the end of your render function**
- Handling conditional rendering logic yourself
- Managing any post-processing effects
- Ensuring proper frame timing and performance
The built-in render modes (`always`, `on-demand`, `manual`) will be bypassed when using custom rendering.
::
## Callback Parameters
Both `onBeforeRender` and `onRender` callbacks receive a context object containing timing information and access to the TresJS context:
```ts
onBeforeRender(({ delta, elapsed, renderer, camera, scene, sizes, invalidate, advance }) => {
// Timing information
console.log('Time since last frame:', delta) // in seconds
console.log('Total elapsed time:', elapsed) // in seconds
// TresJS context access
console.log('Current camera:', camera.value)
console.log('Scene:', scene.value)
console.log('Canvas size:', sizes.width.value, sizes.height.value)
// Control methods
invalidate() // Mark scene for re-render (useful in on-demand mode)
advance() // Manually advance one frame (useful in manual mode)
})
```
### `onBeforeRender` and `onRender` Parameters
:::field-group
::::field{name="delta" type="number"}
Time in seconds since the last frame. Perfect for frame-rate independent animations.
::::
::::field{name="elapsed" type="number"}
Total elapsed time in seconds since the render loop started. Useful for time-based effects.
::::
::::field{name="renderer" type="TresRenderer"}
The Three.js WebGL renderer instance. Access to all renderer methods and properties.
::::
::::field{name="camera" type="ComputedRef"}
The currently active camera in the scene. Reactive reference that updates when camera changes.
::::
::::field{name="scene" type="ShallowRef"}
The Three.js scene object containing all 3D objects.
::::
::::field{name="sizes" type="SizesType"}
Reactive size information including width, height, and pixel ratio of the canvas.
::::
::::field{name="invalidate" type="() => void"}
Function to mark the scene as needing an update in the next frame. Particularly useful in on-demand rendering mode.
::::
::::field{name="advance" type="() => void"}
Function to manually advance the render loop by one frame. Especially useful in manual rendering mode.
::::
::::field{name="controls" type="Ref"}
Reference to the current camera controls (if any). Useful for camera-based animations.
::::
::::field{name="events" type="EventManager"}
The event manager instance for handling pointer interactions with 3D objects.
::::
:::
### The `render` Method Parameters
The `render` method takes a function that receives a single `notifySuccess` callback parameter:
:::field-group
::::field{name="notifySuccess" type="() => void"}
A callback function that must be called to indicate the frame has been successfully rendered. This is essential for the render loop to function correctly across all render modes.
::::
:::
::note
**Important**: The `render` method does NOT receive a context object like `onBeforeRender` and `onRender`. Instead, use `useTres()` to access the renderer, scene, and camera within your render function.
::
## Type
```ts [Signature]
function useLoop(): UseLoopReturn
interface UseLoopReturn {
/** Stops the render loop */
stop: () => void
/** Starts the render loop */
start: () => void
/** Reactive reference indicating if the loop is currently active */
isActive: Ref
/** Register a callback to run before each render */
onBeforeRender: (fn: LoopCallback, priority?: number) => { off: () => void }
/** Register a callback to run after each render */
onRender: (fn: LoopCallback, priority?: number) => { off: () => void }
/** Take complete control over the rendering process */
render: (fn: RenderFunction) => void
}
type LoopCallback = (context: LoopContext) => void | Promise
type RenderFunction = (notifySuccess: () => void) => void
interface LoopContext {
/** Time in seconds since the last frame */
delta: number
/** Total elapsed time in seconds since render loop started */
elapsed: number
/** The Three.js WebGL renderer instance */
renderer: TresRenderer
/** The currently active camera */
camera: ComputedRef
/** The Three.js scene object */
scene: ShallowRef
/** Reactive size information for the canvas */
sizes: SizesType
/** Reference to current camera controls */
controls: Ref
/** TresJS extension function */
extend: (objects: any) => void
/** Event manager for pointer interactions */
events: EventManager
/** Mark scene for re-render in on-demand mode */
invalidate: () => void
/** Manually advance one frame in manual mode */
advance: () => void
}
interface SizesType {
/** Canvas width in pixels */
width: Ref
/** Canvas height in pixels */
height: Ref
/** Canvas aspect ratio (width / height) */
aspectRatio: Ref
/** Device pixel ratio */
pixelRatio: Ref
}
type TresRenderer = WebGLRenderer | Renderer
```