--- 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 ```