Universal Frame Manager.
Repository ✦ Package ✦ Releases ✦ Discussions
pnpm add @hypernym/frame
- Free & Open Source
- Ultra-lightweight & Powerful
- Framework-Independent
- Written in TypeScript
- Native SSR Support
- No External Dependencies
- Extremely Easy to Use
- API-Friendly
Package size:~1.37 KB
minified,~737 B
gzip
- Frame Scheduling: Schedules processes in different phases of the frame loop.
- Dynamic Phases: Allows dynamic configuration of frame phases.
- Custom Scheduler: Supports various schedulers, such as raf, timeout, and microtask.
- Frame Controls: Provides developers complete control over the execution flow.
- FPS Managment: Adds fixed rate control for frame update cycle.
- Frame State: Tracks and stores the state of the frame cycle.
- Modular Code: Follows modern module standards for flexibility and integration.
- Type-safe: Provides first-class TypeScript support with official built-in declarations.
Frame is designed to manage and execute processes in different phases of a frame-based loop, typically used for timed operations, making it ideal for game loops, animations or periodic updates.
Organizes processes into distinct phases, controls timing with FPS and delta time, ensuring smooth execution by syncing with the browser’s requestAnimationFrame
method, and allows dynamic configuration of frame phases.
Frame provides add
, delete
, start
and stop
methods for powerful control over the execution of the frame cycle, allowing advanced manipulation of when the frame loop should continue or wait for next update.
These methods are super useful in situations where you need to manage the frame dynamically, such as when the user switches tabs and returns to the page.
Install @hypernym/frame
package:
# via pnpm
pnpm add @hypernym/frame
# via npm
npm install @hypernym/frame
Here are some examples of how to integrate Frame from a CDN via a script tag.
Also, it is possible to download files manually and serve them accordingly.
<script type="module">
import { createFrame } from 'https://unpkg.com/@hypernym/frame/dist/index.min.mjs'
const frame = createFrame()
</script>
<script src="https://unpkg.com/@hypernym/frame/dist/index.iife.js"></script>
<script>
const { createFrame } = Frame
const frame = createFrame()
</script>
<script src="https://unpkg.com/@hypernym/frame/dist/index.umd.js"></script>
<script>
const { createFrame } = Frame
const frame = createFrame()
</script>
Create a frame
manager with default phases.
import { createFrame } from '@hypernym/frame'
const frame = createFrame()
let index = 0
// Adds a custom process to the `default` phase and enables looping
const process = frame.add(
(state) => {
index++
console.log('Process Loop', index)
if (index > 100) {
frame.delete(process)
console.log('Process Loop: Done!', state)
}
},
{ loop: true },
)
Each phase is executed in a strict order, which can be defined via frame
options.
Default phase order is read
→ update
→ render
.
frame.add(() => console.log('Phase 2: Render'), { phase: 2 })
frame.add(() => console.log('Phase 1: Update'), { phase: 1 })
frame.add(() => console.log('Phase 0: Read'))
frame.add(() => console.log('Phase 2: Render'), { phase: 2 })
frame.add(() => console.log('Phase 0: Read'))
Output:
Phase 0: Read
Phase 0: Read
Phase 1: Update
Phase 1: Update
Phase 2: Render
Phase 2: Render
import { createFrame } from '@hypernym/frame'
// Frame
const frame = createFrame(options)
// Phases
frame.add(process, options)
frame.delete(process)
frame.delete()
// Controls
frame.start()
frame.stop()
// Getters/Setters
frame.state
frame.fps
- Type:
(process: Process, options?: ProcessOptions) => Process
Adds a specific process to the frame update cycle.
By default, the process will be executed only once, but you can configure it to repeat by enabling a loop: true
option, continuing until it is explicitly stopped.
frame.add(process, options)
- Type:
(process?: Process) => void
Allows you to remove a specific process from the frame update cycle which means no more process repeats on subsequent frames.
This is useful when you need to dynamically remove or cancel a task from the queue without disrupting the rest of the frame update flow.
frame.delete(process)
It is also possible to removes all scheduled phases from the frame update cycle and reset the state of the frame.
Effectively clears the entire update queue, stopping all scheduled tasks from running on subsequent frames and cancels the scheduler. No tasks will be executed until new ones are scheduled.
Useful for cleanup or re-initialization, like when starting over or clearing tasks after a specific process completes.
It's recommended to call this method when you are sure that there is no need for another frame process, such as before changing routes in meta-frameworks.
frame.delete()
- Type:
() => void
Unpauses the loop and restores the normal frame update cycle.
Ensures that the frame updates continue as expected, without skipping any frames, and the system will continue processing scheduled tasks on each new frame.
frame.start()
- Type:
() => void
Halts the entire frame update loop, pausing all scheduled tasks and animations until .start()
is explicitly called again.
Helps prevent unnecessary processing and save system resources.
frame.stop()
- Type:
boolean
- Default:
undefined
Specifies whether the phase process should continue to repeat through each subsequent frame, without stopping after the first execution.
Directs the scheduling system to re-run the same task on each frame until explicitly deleted.
frame.add((state) => console.log(state), { loop: true })
- Type:
number
- Default:
0
All phases are unified under a single .add()
method and are created dynamically, optimizing performance by avoiding unnecessary initialization.
The default phase is 0
, and specifying a phase is optional. You can define an unlimited number of phases, though most use cases only require one or two, typically three: read (0)
, update (1)
, and render (2)
.
Phases always run in strict
numerical order, from the smallest to the largest.
frame.add(process, { phase: -1 }) // runs before 0
frame.add(process) // default phase is 0
frame.add(process, { phase: 1 }) // runs after 0
frame.add(process, { phase: 2 }) // runs after 1
// etc ...
- Type:
boolean
- Default:
true
Specifies the scheduling behavior.
By default this is enabled which means the process waits for the next loop cycle.
If disabled, it cancels the scheduling to the next frame and executes at the end of the current frame.
let index = 0
frame.add(() => {
index++
frame.add(() => index++, { schedule: false })
})
frame.add(() => console.log('Index: ', index), { phase: 1 }) // => Index 2
- Type:
object
Provides a read-only info of the internal frame state
at any given point.
This can include info about the frame’s current delta
, timestamp
and running
status.
Useful for debugging and monitoring or for dynamic actions based on the current state of the frame.
frame.add((state) => console.log(state))
// The state can also be accessed via the `.state` getter
console.log(frame.state)
- Type:
(process: VoidFunction) => number | void
- Default:
requestAnimationFrame
Specifies the scheduling system for the frame cycle.
This determines how the frame updates are processed, whether through the requestAnimationFrame
, setTimeout
or microtask
.
import { createFrame } from '@hypernym/frame'
const frame = createFrame({ scheduler: queueMicrotask, loop: false })
- Type:
number | false
- Default:
undefined
Specifies a fixed rate for the frame update cycle.
Useful when you want to control the framerate limit and prevent the frame loop from running at the maximum possible speed (e.g., for performance reasons, consistency, or to match a loop’s intended frame rate).
This ensure that the updates are more predictable and not too fast or inconsistent.
By default, the frame runs as fast as possible (typically tied to the raf
cycle, which is usually 60 FPS or higher).
import { createFrame } from '@hypernym/frame'
const frame = createFrame({ fps: 60 })
// It can also be set dynamically via the `.fps` setter
frame.fps = 60
Feel free to ask questions or share new ideas.
Use the official discussions to get involved.
Developed in 🇭🇷 Croatia, © Hypernym Studio.
Released under the MIT license.