# Communal Hyperfy Note Pool
# itsmetamike
<!-- ## Hyperfy World System
### Overview
Hyperfy uses a core World system as its main orchestrator. The World system manages all other systems and provides core functionality for the 3D environment.
### Key Features
- Acts as the central system manager
- Handles system registration and initialization
- Provides access to shared resources like physics and loaders
- Maintains the scene graph hierarchy
### System Registration
```javascript
// Systems can be registered using
world.register(SystemClass)
// or previously
world.systems.add(SystemClass)
```
### Core Components
- **Scene Management**: The World system maintains the Three.js scene graph
- **Physics Integration**: Provides access to PhysX physics engine
- **Asset Loading**: Manages asset loading through the loader system
- **System Lifecycle**: Handles init and start lifecycle of registered systems
### Common Usage
```javascript
// Systems can access world instance
this.world.loader // Access asset loader
this.world.physics // Access physics engine
```
## Hyperfy Asset Management
### Overview
Hyperfy implements a sophisticated asset management system through its loader subsystem, handling various types of 3D assets and resources.
### Asset Loading System
- Located in `src/core/systems/ServerLoader.js`
- Supports multiple asset types (GLB, VRM, HDR)
- Implements caching for performance
- Uses asset:// protocol for path resolution
### Asset Types
- **GLB Models**: Primary 3D model format
- **VRM Models**: For avatar/character models
- **HDR**: For environment maps
- **Emotes**: For character animations
### Asset Resolution
```javascript
// Assets are resolved relative to configured assets directory
this.assetsDir = path.join(__dirname, '../', process.env.ASSETS_DIR)
// Using asset:// protocol
loader.load('glb', 'asset://model.glb')
```
### Caching
- Assets are cached by type and URL
- Cache is maintained in memory using Map
- Prevents redundant loading of shared assets
## Hyperfy Physics System (needs work)
### Overview
Hyperfy integrates the PhysX physics engine for realistic physics simulation in the 3D environment.
### Physics Components
- **PhysX Integration**: Direct integration with NVIDIA PhysX
- **Rigid Bodies**: Support for both static and dynamic rigid bodies
- **Materials**: Customizable physics materials for friction and restitution
- **Collision Shapes**: Various primitive shapes supported
### Common Physics Operations
```javascript
// Creating physics materials
const material = world.physics.physics.createMaterial(friction, restitution, frictionCombine)
// Creating rigid bodies
const body = world.physics.physics.createRigidDynamic(transform)
const staticBody = world.physics.physics.createRigidStatic(transform)
// Shape creation
const shape = world.physics.physics.createShape(geometry, material, isExclusive, flags)
```
### Collision Filtering
- Supports collision groups and filters
- Can specify which objects interact with each other
- Uses PxFilterData for configuration
### Scene Integration
- Physics bodies can be attached to Three.js meshes
- Automatic synchronization of physics and visual representation
- Scene manages physics world updates
## Hyperfy System Architecture
### Overview
Hyperfy uses a modular system-based architecture where functionality is encapsulated in systems that can be registered with the world.
### System Base Class
- All systems extend from base System class
- Systems have defined lifecycle methods
- Systems can access world instance and other systems
### System Lifecycle
```javascript
class CustomSystem extends System {
constructor(world) {
super(world)
}
init() {
// Initialize system
}
start() {
// Start system after initialization
}
}
```
### System Communication
- Systems can access each other through world instance
- Event-based communication supported
- Shared state through world object
### Directory Structure
```
src/
core/
systems/ # Core system implementations
World.js # World system definition
apps.js # System registration
```
### Best Practices
- Systems should be self-contained
- Use dependency injection through world
- Follow consistent naming conventions
- Implement proper cleanup in destroy methods
### System Architecture Overview
#### Core Systems
- World system
- Physics system
- Rendering system
- Asset management
#### System Communication
- Event system
- Message passing
- Direct references
- Shared state
#### Data Flow
- System updates
- Event propagation
- State management
- Resource handling
#### Performance Considerations
- Update ordering
- Memory management
- CPU optimization
- GPU utilization
#### Debugging Tools
- System monitors
- Performance profiling
- State inspection
- Error tracking
## Hyperfy Rendering System
### Overview
Hyperfy uses Three.js as its core rendering engine, with additional features for shadows, materials, and optimization.
### Key Features
- Three.js integration
- Shadow system
- Material system
- Scene graph management
### Scene Management
```javascript
// Adding objects to scene
this.base = new THREE.Group()
world.scene.add(this.base)
// Object traversal
object.traverse((child) => {
if (child.isMesh) {
child.castShadow = true
child.receiveShadow = true
}
})
```
### Material System
- Support for standard Three.js materials
- Custom material implementations
- PBR material support
- Material caching
### Optimization Features
- Automatic LOD management
- Object pooling
- Scene culling
- Shadow optimization
### Asset Integration
- GLB/GLTF model support
- VRM character support
- Custom model processing
- Texture management -->
<!-- ## Implementing Skyboxes in Hyperfy
This guide explains how to implement skyboxes in Hyperfy using the `ClientEnvironment` system. Skyboxes provide a 360-degree background environment for your virtual world.
### Overview
The skybox implementation in Hyperfy involves two main components:
1. The texture loading system (`ClientLoader.js`)
2. The environment setup (`ClientEnvironment.js`)
### Texture Loading System
#### Setting up the Loader
In `ClientLoader.js`, we need to ensure we have the proper loaders for different texture types:
```javascript
import * as THREE from 'three'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
class ClientLoader extends System {
constructor(world) {
super(world)
this.textureLoader = new THREE.TextureLoader() // For JPG/PNG textures
this.rgbeLoader = new RGBELoader() // For HDR textures
}
}
```
#### Implementing Texture Loading
Add support for different texture types in the `load` method:
```javascript
load(type, url) {
const key = `${type}/${url}`
if (this.promises.has(key)) {
return this.promises.get(key)
}
url = this.resolveURL(url)
let promise
if (type === 'texture') {
promise = this.textureLoader.loadAsync(url).then(texture => {
this.results.set(key, texture)
return texture
})
}
if (type === 'hdr') {
promise = this.rgbeLoader.loadAsync(url).then(texture => {
this.results.set(key, texture)
return texture
})
}
// ... other loaders
}
```
### Environment Setup
#### Building the Skybox
In `ClientEnvironment.js`, implement the skybox setup:
```javascript
async buildSkybox() {
try {
const url = '/cyberpunk-skybox.jpg' // Path relative to public directory
console.log('Loading skybox texture from:', url)
const texture = await this.world.loader.load('texture', url)
if (!texture) {
console.error('Texture failed to load')
return
}
// Configure texture mapping for skybox
texture.mapping = THREE.EquirectangularReflectionMapping
texture.colorSpace = THREE.SRGBColorSpace
// Set as scene background
this.world.stage.scene.background = texture
} catch (error) {
console.error('Error building skybox:', error)
}
}
```
#### HDR Environment
For better lighting, you can also set up an HDR environment:
```javascript
async buildHDR() {
const url = '/environment.hdr'
const texture = await this.world.loader.load('hdr', url)
texture.mapping = THREE.EquirectangularReflectionMapping
this.world.stage.scene.environment = texture
await this.buildSkybox()
}
```
### Asset Requirements
1. **Skybox Texture**:
- Place in `src/client/public/`
- Use equirectangular projection format
- JPG or PNG format for basic skyboxes
- HDR format for high dynamic range environments
2. **File Structure**:
```
src/
client/
public/
cyberpunk-skybox.jpg # Your skybox texture
environment.hdr # Optional HDR environment map
```
### Tips and Best Practices
1. **Texture Format**:
- Use JPG/PNG for simple skyboxes
- Use HDR for better lighting and reflections
- Ensure textures are in equirectangular projection format
2. **Performance**:
- Keep texture sizes reasonable (2048x1024 is often sufficient)
- Consider using compressed texture formats for better loading times
3. **Debugging**:
- Check console logs for loading errors
- Verify texture paths are correct relative to public directory
- Ensure textures are properly formatted for equirectangular mapping
### Common Issues
1. **Texture Not Loading**:
- Verify file exists in public directory
- Check file permissions
- Ensure correct loader type is being used ('texture' vs 'hdr')
2. **Distorted Skybox**:
- Confirm texture is in equirectangular projection format
- Check texture mapping settings
- Verify texture dimensions (should be 2:1 ratio)
3. **Missing Reflections**:
- HDR environments provide better reflections than standard textures
- Ensure `scene.environment` is set for HDR maps -->
# hiroP
## Really basic: adding mesh to your world
To add a mesh to your world you can edit `src/core/systems/ClientEnvironment.js`
In there just add something like:
```
const hiroLogo = await this.world.loader.load('glb', 'asset://hiroLogo01.glb')
const hiroRoot = hiroLogo.toNodes()
hiroRoot.activate({ world: this.world, physics: true })
hiroRoot.position.set(0, 10, 10) // Adjust x,y,z as needed
```
to ` async start()`
The asset in the above will be accessible in whatever directory you set in the `.env`
## Adding collisions to meshes
### Basic collider
There are a few occassions when you might want a simple collision but usually you'll need to structure your object with a hierarchy like below in the [#Dynamic-collider](#Dynamic-collider) section. Let's imagine you do actually want a simple sphere to act as a collider. You would create the following hierarchy in your Blender object:
```
MyBall (Empty)
├── BallMesh (Detailed sphere if you need one)
└── PhysicsSphere (Simple sphere mesh)
└── Custom Properties:
node = "rigidbody"
type = "dynamic"
node = "collider"
convex = true
```
To set the custom properties you select your mesh (simple sphere in this example) and scroll down to the bottom and add a new custom property like this:

- click OK and then set the value to 'collider' so it looks like this:

If you want to create a fancy ball (eg one with football panels/decals etc. you'd add that as a sibling of PhysicsSphere so that it isn't used in the physics calculations.
For anything more complicated that this you will want to separate the collision and rigidbody meshes as shown in the next section.
### Dynamic collider
To create a collider that responds to physics you need to create an object with the following structure:
```
MyDynamicObject (Empty/Group)
├── DetailedMesh (Complex mesh - what the thing looks like)
└── RigidBody (Empty or minimal mesh)
└── Custom Properties:
node = "rigidbody"
type = "static" | "kinematic" | "dynamic"
mass = [number value >= 0] (see note below)
tag = [optional string, cannot be "player"]
└── CollisionShape (Simplified collision mesh)
└── Custom Properties:
node = "collider"
convex = [true/false]
```
_Note: mass is not currently supported so just leave it out - it will be set to a default of 1._
#### RigidBody types
Looking at the RigidBody.js code, there are three types defined:
```javascript
const types = ['static', 'kinematic', 'dynamic']
```
Here's what each does:
1. `static` - For immovable objects like walls, floors, or fixed furniture. The code shows these create a `createRigidStatic` PhysX actor which:
- Won't move when hit
- Other objects will collide with it
- Most efficient performance-wise
2. `dynamic` - For objects that should move realistically with physics. Creates a `createRigidDynamic` PhysX actor that:
- Will respond to gravity
- Will bounce/collide with other objects
- Will be pushed by forces
- Uses the mass property for physics calculations
3. `kinematic` - For objects that move but aren't affected by physics. Also creates a `createRigidDynamic` actor but sets the `KINEMATIC` flag:
- Can be moved programmatically
- Other objects will collide with it
- Won't respond to gravity or forces
- Mass is less important since it's not physics-driven
So for your example - if you want the object to fall and bounce realistically, use `type="dynamic"`. If it should stay perfectly still, use `type="static"`. Use `kinematic` if you want to move it programmatically but still have other objects collide with it.
# peezy
## Deploying Your Hyperfy World
This guide will walk you through deploying your Hyperfy world on a VPS. Don't worry if you're new to server deployment - we'll go through it step by step!
### Initial VPS Setup
Before connecting to your VPS, you need to upload an SSH key to your provider's platform.
```bash
# Generate an SSH key if you don't have one
ssh-keygen -t ed25519 -C "your_email@example.com"
# Display your public key
cat ~/.ssh/id_ed25519.pub # On Mac/Linux
# or
type $env:USERPROFILE\.ssh\id_ed25519.pub # On Windows PowerShell
```
Copy the entire key and add it to your VPS provider's dashboard.
### Setting Up Your Server
#### 1. Initial Connection
```bash
# Connect to your VPS as root
ssh root@your_server_ip
```
#### 2. Create a Deployment User
```bash
# Create new user (replace YOUR_USERNAME with your preferred username)
adduser YOUR_USERNAME
usermod -aG sudo YOUR_USERNAME
# Set up SSH for the new user
mkdir -p /home/YOUR_USERNAME/.ssh
cp ~/.ssh/authorized_keys /home/YOUR_USERNAME/.ssh/
chown -R YOUR_USERNAME:YOUR_USERNAME /home/YOUR_USERNAME/.ssh
chmod 700 /home/YOUR_USERNAME/.ssh
chmod 600 /home/YOUR_USERNAME/.ssh/authorized_keys
# Exit and reconnect as your user
exit
ssh YOUR_USERNAME@your_server_ip
```
#### 3. Secure the SSH Configuration
After verifying you can log in as your user, secure the SSH service:
```bash
# Make a backup of the original configuration
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
# Edit the SSH configuration
sudo nano /etc/ssh/sshd_config
```
Make the following changes in the configuration file:
```bash
# Disable root login
PermitRootLogin no
# Disable password authentication
PasswordAuthentication no
# Enable public key authentication
PubkeyAuthentication yes
# Specify which users can connect via SSH
AllowUsers YOUR_USERNAME
# Optional but recommended: Limit SSH access attempts
MaxAuthTries 3
```
Apply the changes:
```bash
# Test the configuration for syntax errors
sudo sshd -t
# Restart the SSH service
sudo systemctl restart sshd
```
Important: Before closing your current SSH session:
1. Open a new terminal
2. Try to connect with the new configuration
3. Make sure it works before closing your working session
If something goes wrong, you can restore the backup:
```bash
sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
sudo systemctl restart sshd
```
#### 4. Install Node.js Via NVM
```bash
# Install required build tools
sudo apt update
sudo apt install -y curl git build-essential
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Load NVM (or reconnect to your server)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# Install required Node version (22.11.0+)
nvm install 22
# Set as default
nvm alias default 22
```
#### 4. Install PM2 and Nginx
```bash
# Install PM2 globally
npm install -g pm2
# Install Nginx
sudo apt install -y nginx
```
### Deploying Your Hyperfy World
#### 1. Clone and Setup Your World
```bash
# Go to your home directory
cd ~
# Clone the repository
git clone https://github.com/hyperfy-xyz/hyperfy.git my-world
cd my-world
# Copy environment file
cp .env.example .env
# Install dependencies
npm install
```
#### 2. Configure Environment Variables
change the environment variables to point to your domain url
```bash
# Edit your environment file
nano .env
```
```bash
PUBLIC_WS_URL=https://YOUR_DOMAIN.com/ws
PUBLIC_API_URL=https://YOUR_DOMAIN.com/api
PUBLIC_ASSETS_URL=https://YOUR_DOMAIN.com/assets
```
#### 3. Start Your Application with PM2
```bash
# Start the application
pm2 start npm --name "hyperfy" --interpreter bash -- start
# Set PM2 to start on system boot
pm2 startup
pm2 save
```
#### 4. Configure Nginx as Reverse Proxy
```bash
# Remove default config
sudo rm /etc/nginx/sites-enabled/default
# Create new config (replace yourdomain.com with your actual domain)
sudo nano /etc/nginx/sites-available/yourdomain.com
```
Add this configuration:
```nginx
server {
listen 80;
server_name your-domain.com; # Change to your domain
location / {
proxy_pass http://localhost:3000; # Change port if different
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
Enable and start Nginx:
```bash
# Enable the site
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Start Nginx
sudo systemctl restart nginx
```
#### 5. Configure Your Domain's DNS
Before enabling HTTPS, you need to point your domain to your server:
1. Go to your domain registrar's website (like Namecheap, GoDaddy, etc.)
2. Find the DNS settings or DNS management section
3. Add or modify the following records:
```
Type | Name | Value
A | @ | your_server_ip
CNAME | www | your_domain
```
4. Save your changes
DNS changes can take anywhere from a few minutes to 48 hours to propagate. You can check the propagation by going on https://dnschecker.org/ and inserting your domain. it should point to your server ip
#### 6. Set Up HTTPS with Certbot
Let's secure your site with a free SSL certificate from Let's Encrypt:
```bash
# Install Certbot and its Nginx plugin
sudo apt install certbot python3-certbot-nginx
# Obtain and install the certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# When prompted:
# 1. Enter your email address
# 2. Agree to terms of service
# 3. Choose whether to redirect HTTP to HTTPS (recommended)
```
Certbot will automatically modify your Nginx configuration to use HTTPS. It also sets up automatic renewal of your certificates.
You can verify that the SSL certificate is set to auto-renew by checking the Certbot timer
```bash
sudo systemctl status certbot.timer
```
#### 7. Configure Firewall
```bash
# Allow SSH, HTTP, and HTTPS
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
# Enable firewall
sudo ufw enable
```
### Updating Your World
When you need to update your world:
```bash
# Go to your world directory
cd ~/my-world
# Pull latest changes
git pull
# Install any new dependencies
npm install
# Restart the application
pm2 restart hyperfy
```
### Monitoring and Troubleshooting
#### View Application Status
```bash
# Check status
pm2 status
# View logs
pm2 logs hyperfy
# Monitor CPU/Memory usage
pm2 monit
```
#### Check Nginx Status
```bash
# View Nginx status
sudo systemctl status nginx
# Check error logs
sudo tail -f /var/log/nginx/error.log
```
#### Common Issues
1. Port 3000 already in use:
```bash
# Find what's using the port
sudo lsof -i :3000
# Kill the process
sudo kill -9 <PID>
```
2. Node version issues:
```bash
# Verify Node version
node -v
# Switch version if needed
nvm use 22
```
3. Permission issues:
```bash
# Fix ownership if needed
sudo chown -R YOUR_USERNAME:YOUR_USERNAME ~/my-world
```
4. SSL Certificate issues:
```bash
# Check certificate status
sudo certbot certificates
# Renew certificates manually if needed
sudo certbot renew
# View Certbot logs
sudo tail -f /var/log/letsencrypt/letsencrypt.log
```
Remember to check your application logs (`pm2 logs`) for any specific error messages if you encounter issues. Most problems can be diagnosed through the logs!
# JoelPlatoon
## Hyperfy Meets IQ6900: A Scalable & Cost-Efficient Storage Solution
**IQ6900's Code-In Technology** provides a revolutionary approach to blockchain-based storage, enabling efficient, secure, and cost-effective solutions for managing 3D assets. Its unique ability to attach to any blockchain makes it a versatile choice for Hyperfy's infrastructure.
### Key Features:
- **Storage Efficiency**: Significantly reduces on-chain storage costs compared to traditional methods.
- **Cross-Chain Compatibility**: Works seamlessly with Ethereum, Solana, and other blockchains.
- **Security**: Supports encryption, chunking, and on-chain metadata for robust asset management.
### GitHub Repository:
- [Code-In for Ethereum](https://github.com/zo-eth/code-in-for-eth)
This plugin can be integrated into Hyperfy to store 3D assets more efficiently, leveraging [IQ6900's](https://iq6900.com/) innovative technology.
## Setting up Code/Test Environment
## 1. Smart Contract Deployment
- Ensure the `code-in-for-eth` smart contract is properly deployed on Ethereum with the correct ABI and address.
- Verify that the `storeMetadata` and `getMetadata` functions in the contract work as intended.
---
## 2. Library Installation
Install all required libraries before running the code:
```
npm install web3 axios crypto fs
```
## 3. Environment Variables
Use environment variables (.env) to securely store sensitive data like private keys and API keys.
Example .env setup:
```
INFURA_PROJECT_ID=<your-infura-id>
PRIVATE_KEY=<your-private-key>
ENCRYPTION_KEY=<your-encryption-key>
```
## 4. Input Validation
Validate all user inputs and file paths to ensure no malformed data is processed.
Use libraries like path to sanitize file paths.
## 5. Chunk Size Optimization
Set an optimal chunk size based on IQ6900’s storage capacity and retrieval speeds.
Test the chunking process with different sizes (e.g., 10 KB, 100 KB).
## 6. Ethereum Gas Handling
Ensure sufficient ETH in the sender’s account to cover gas fees for transactions.
Use a gas price oracle to dynamically adjust gas prices.
## 7. Security Measures
### Encryption
Use AES-256 for encrypting assets. Ensure the encryption key is long and stored securely.
### Private Key Handling
Never hardcode private keys. Use environment variables or secure vaults (e.g., AWS Secrets Manager).
### Access Control
Ensure storeMetadata and getMetadata functions on the Ethereum contract enforce proper access controls.
## 8. Error Handling
Add comprehensive try-catch blocks to handle errors gracefully and log issues for debugging.
## 9. Testing
Test the integration end-to-end with small assets before scaling up to larger files.
Simulate edge cases (e.g., network failure, insufficient funds).
## Code Example
```
require('dotenv').config();
const Web3 = require('web3');
const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');
// Initialize Ethereum connection
const web3 = new Web3(`https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`);
// Ethereum smart contract configuration
const contractABI = [ /* ABI from code-in-for-eth */ ];
const contractAddress = '0xYourContractAddress';
const contract = new web3.eth.Contract(contractABI, contractAddress);
// Utility functions
function compressData(data) { return data; } // Placeholder
function decompressData(data) { return data; } // Placeholder
function encryptData(data, encryptionKey) {
const cipher = crypto.createCipher('aes-256-cbc', encryptionKey);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function decryptData(data, encryptionKey) {
const decipher = crypto.createDecipher('aes-256-cbc', encryptionKey);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
function splitIntoChunks(data, chunkSize) {
const chunks = [];
for (let i = 0; i < data.length; i += chunkSize) {
chunks.push(data.slice(i, i + chunkSize));
}
return chunks;
}
// Main Plugin Class
class IQ6900HyperfyPlugin {
constructor() {
this.encryptionKey = process.env.ENCRYPTION_KEY;
this.privateKey = process.env.PRIVATE_KEY;
}
async uploadAsset(filePath, userAddress) {
// Implementation here
}
async retrieveAsset(metadataTxid) {
// Implementation here
}
}
// Example Usage
(async () => {
const plugin = new IQ6900HyperfyPlugin();
const userAddress = '0xYourEthereumAddress';
const filePath = './path/to/3d-model.glb';
const metadataTxid = await plugin.uploadAsset(filePath, userAddress);
console.log('Metadata TxID:', metadataTxid);
const assetData = await plugin.retrieveAsset(metadataTxid);
console.log('Retrieved Asset Data:', assetData);
})();
```
## Features
## Secure Data Handling
* Encryption: Protects 3D asset data during storage and retrieval.
* Compression: Reduces file size to save costs and improve speed.
## Chunked Uploads
* Splits large assets into manageable 10 KB chunks for efficient storage and retrieval.
## On-Chain Metadata
* Stores chunk references (TxIDs) on Ethereum using the code-in-for-eth contract.
## Seamless Retrieval
* Retrieves all chunks from IQ6900, decrypts, decompresses, and reassembles the asset.
## Ethereum Integration
* Interacts with Hyperfy’s Ethereum environment for secure metadata management.
## Cost Comparison: Ethereum vs. IQ6900 (Solana)
| **Asset Size** | **Ethereum Cost (USD)** | **IQ6900 Cost (USD)** | **Savings (%)** |
|----------------|--------------------------|------------------------|------------------|
| 500 KB | $1,285.79 | $0.3339 | ~99.97% |
| 2 MB | $5,143.14 | $1.3354 | ~99.97% |
| 10 MB | $25,715.70 | $6.677 | ~99.97% |
## Insights on Cost Savings
### 🚀 **Massive Cost Savings**
- **IQ6900** reduces costs by **~99.97%** compared to Ethereum storage.
---
### 💾 **Efficient Storage Solution**
- Storing a **10 MB file**:
- **IQ6900**: **$6.68**
- **Ethereum**: **$25,715.70**
---
### 🔗 **Interoperable Architecture**
- **Metadata on Ethereum**: Store critical metadata for compatibility.
- **Assets on IQ6900**: Offload large 3D assets (e.g., `.glb` files) for efficient, secure, and scalable storage.
---
### 📈 **Scalable for Hyperfy**
- Enables Hyperfy to:
- Store assets **affordably**.
- Maintain **fast access** and **high reliability**.
# b0gie
## BuildMode System Documentation
### Overview
BuildMode is a specialized camera control system for Hyperfy that enables free-flying
camera movement for building and world editing.
When activated, it temporarily disables player controls and provides smooth, unrestricted camera movement throughout the world.
### Features
#### Camera Controls
- **Activation**: Press `B` to toggle BuildMode
- **Movement**:
- `W/A/S/D`: Move camera forward/left/backward/right
- `Space`: Move camera up
- `C`: Move camera down
- `Mouse`: Look around (smooth camera rotation)
- `Mouse Wheel`: Adjust movement speed
#### Speed Control
- Default movement speed: 10 units/second
- Speed range: 1 to 50 units/second
- Scroll up: Decrease speed
- Scroll down: Increase speed
- Speed adjustment factor: 1.2x per scroll tick
#### Player State Management
When BuildMode is activated:
- Player model is hidden
- Player animations are frozen
- Player physics are locked
- Player position is preserved
- Player controls are disabled
### Technical Implementation
#### Core Components
##### Camera System
```javascript
// Camera initialization
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
// Smooth rotation handling
this.targetRotation = new THREE.Euler(0, 0, 0, 'YXZ')
this.currentRotation = new THREE.Euler(0, 0, 0, 'YXZ')
```
##### Movement System
The movement system uses quaternion-based directional movement for smooth camera control:
```javascript
moveForward(distance) {
const direction = new THREE.Vector3(0, 0, -1)
direction.applyQuaternion(this.camera.quaternion)
this.camera.position.addScaledVector(direction, distance)
}
```
#### State Management
##### Activation Process
1. Store original camera and player states
2. Disable player controls and visibility
3. Enable free camera movement
4. Lock player physics
5. Enable pointer lock for smooth mouse control
```javascript
toggleBuildMode() {
if (this.active) {
// Store states
this.originalCameraPosition.copy(this.world.camera.position)
this.originalCameraQuaternion.copy(this.world.camera.quaternion)
// Disable player
player.control.camera.unclaim()
player.control.priority = -1
player.visible = false
// Enable build mode camera
this.control.camera.claim()
}
// ...
}
```
### Integration Guide
#### Adding BuildMode to Your World
1. Register the BuildMode system with your world:
```javascript
import { BuildMode } from './systems/BuildMode'
// In your world creation:
world.addSystem(BuildMode)
```
2. Ensure proper control priorities are set:
```javascript
// In ControlPriorities.js
export const ControlPriorities = {
EDITOR: 100, // BuildMode priority
PLAYER: 0 // Normal player priority
}
```
#### Event Handling
BuildMode integrates with Hyperfy's event system:
- Listens for key press events (B key for toggle)
- Handles mouse movement for camera rotation
- Processes scroll events for speed adjustment
### Best Practices
1. **State Preservation**
- Always store original states before modification
- Restore all states when deactivating
- Handle edge cases (e.g., disconnection while in BuildMode)
2. **Performance**
- Use smooth interpolation for camera movement
- Implement proper cleanup in the destroy method
- Manage event listeners carefully
3. **User Experience**
- Provide smooth camera movement
- Implement intuitive controls
- Maintain consistent behavior across different scenarios
### Common Issues and Solutions
1. **Player Visibility**
```javascript
// Problem: Player still visible in BuildMode
// Solution: Properly hide both avatar and base
if (player.avatar) {
player.avatar.visible = false
player.avatar.mixer.stopAllAction()
}
if (player.base) {
player.base.visible = false
}
```
2. **Camera Rotation**
```javascript
// Problem: Camera flipping at extreme angles
// Solution: Clamp rotation values
this.targetRotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.targetRotation.x))
```
### Future Enhancements
Potential improvements to consider:
1. Grid system for precise placement
2. Snap-to-surface functionality
3. Object selection and manipulation
4. Multiple camera modes (orbit, first-person, etc.)
5. Customizable key bindings
6. Save/restore camera positions
### Contributing
When contributing to BuildMode:
1. Maintain the existing control scheme
2. Test edge cases thoroughly
3. Document any changes or additions
4. Ensure smooth state transitions
5. Consider performance implications
### Related Systems
- Player Control System
- Camera Control System
- Physics System
- Input Management System
## First-Person Camera Implementation
### Overview
This document outlines the implementation of a first-person camera system in Hyperfy, including camera positioning, rotation handling, and smooth transitions between first and third-person modes.
### Key Features
- Toggle between first and third-person modes using 'C' key
- Camera positioned at eye level in first-person mode
- Smooth transitions between camera modes
- Proper collision handling and clipping prevention
- Consistent interaction distances in both modes
### Implementation Details
### Camera Configuration
```javascript
this.firstPersonCam = {
position: new THREE.Vector3(),
quaternion: new THREE.Quaternion(),
rotation: new THREE.Euler(0, 0, 0, 'YXZ'),
offset: new THREE.Vector3(0, 1.65, -0.7), // Eye level height and forward offset
targetPosition: new THREE.Vector3(),
targetQuaternion: new THREE.Quaternion(),
lerpSpeed: 15
}
```
### Key Components
1. **Camera Mode Toggle**
- Activated by pressing 'C'
- Handles zoom and camera position transitions
- Preserves VRM first-person settings
2. **Position Updates**
- Camera positioned at eye level (1.65 units up)
- Forward offset (-0.7 units) to prevent clipping
- Smooth position transitions using lerp
3. **Rotation Handling**
- Separate rotation limits for first-person (60 degrees) and third-person (90 degrees)
- Base rotation only affected by left/right movement
- Head bone rotation follows camera in first-person mode
4. **Interaction System**
- Consistent interaction distances in both modes
- Rig position updated to match base position
- Maintains proper 'E' action functionality
### Code Examples
#### Camera Mode Toggle
```javascript
if (code === 'KeyC') {
this.isFirstPerson = !this.isFirstPerson
if (this.isFirstPerson) {
this.normalZoom = this.cam.zoom
this.cam.zoom = 0
this.control.camera.zoom = 0
} else {
this.cam.zoom = this.normalZoom
this.control.camera.zoom = this.normalZoom
}
}
```
#### Position Updates
```javascript
// First-person camera position update
activeCam.targetPosition.copy(this.base.position)
activeCam.targetPosition.y += this.firstPersonCam.offset.y
const forward = new THREE.Vector3(0, 0, this.firstPersonCam.offset.z)
forward.applyQuaternion(this.base.quaternion)
activeCam.targetPosition.add(forward)
```
#### Rotation Updates
```javascript
if (this.isFirstPerson) {
activeCam.rotation.x = clamp(activeCam.rotation.x, -this.firstPersonRotationLimit, this.firstPersonRotationLimit)
if (this.headBone) {
this.headBone.rotation.set(0, 0, 0)
this.headBone.rotation.x = activeCam.rotation.x
}
const baseRotation = new THREE.Euler(0, activeCam.rotation.y, 0, 'YXZ')
this.base.quaternion.setFromEuler(baseRotation)
}
```
### Best Practices
1. Always update the rig position to match the base position for consistent interactions
2. Use smooth transitions for camera movements to prevent jarring changes
3. Implement proper collision checks to prevent camera clipping
4. Maintain separate rotation limits for first and third-person modes
5. Update head bone rotation to match camera rotation in first-person mode
### Known Considerations
- Camera position needs careful tuning to prevent seeing inside the character model
- Rotation limits should be adjusted based on the desired level of head movement
- Interaction distances should be consistent across both camera modes
- VRM first-person settings should be respected for proper model rendering
## Enhanced App Inspection System
This guide explains how to add position/rotation controls and a freeze toggle to Hyperfy's app inspection system.
### Implementation Steps
#### 1. Update InspectPane.js
First, modify `src/client/components/InspectPane.js` to add the new fields:
```javascript
// Add state for transform values
function Fields({ app, blueprint }) {
const [position, setPosition] = useState(app.root?.position || new THREE.Vector3())
const [rotation, setRotation] = useState(app.root?.rotation || new THREE.Euler())
const [frozen, setFrozen] = useState(app.frozen || false)
// Add live updates
useEffect(() => {
const onUpdate = () => {
if (app.root) {
setPosition(app.root.position.clone())
setRotation(app.root.rotation.clone())
}
setFrozen(app.frozen)
}
onUpdate()
app.on('update', onUpdate)
return () => app.off('update', onUpdate)
}, [app])
// Add transform fields to the UI
const transformFields = [
{
type: 'section',
key: 'transform',
label: 'Transform',
},
{
type: 'vector3',
key: 'position',
label: 'Position',
value: position,
},
{
type: 'euler',
key: 'rotation',
label: 'Rotation',
value: rotation,
},
{
type: 'switch',
key: 'frozen',
label: 'Freeze',
value: frozen,
options: [
{ value: true, label: 'Frozen' },
{ value: false, label: 'Unfrozen' }
]
},
...fields
]
}
```
#### 2. Add Transform Handlers
Add these handlers to the `modify` function in `Fields`:
```javascript
const modify = (key, value) => {
if (config[key] === value) return
// Position updates
if (key === 'position' && app.root) {
app.root.position.copy(value)
app.data.position = value.toArray()
world.network.send('entityModified', {
id: app.data.id,
position: app.data.position
})
if (app.networkPos) {
app.networkPos.pushArray(app.data.position)
}
setPosition(value.clone())
return
}
// Rotation updates
if (key === 'rotation' && app.root) {
app.root.rotation.copy(value)
const quaternion = new THREE.Quaternion().setFromEuler(value)
app.data.quaternion = quaternion.toArray()
world.network.send('entityModified', {
id: app.data.id,
quaternion: app.data.quaternion
})
if (app.networkQuat) {
app.networkQuat.pushArray(app.data.quaternion)
}
setRotation(value.clone())
return
}
// Freeze updates
if (key === 'frozen') {
app.frozen = value
world.network.send('entityModified', {
id: app.data.id,
frozen: value
})
setFrozen(value)
return
}
}
```
#### 3. Update App.js
Modify `src/core/entities/App.js` to handle frozen state:
```javascript
export class App extends Entity {
constructor(world, data, local) {
super(world, data, local)
// Add frozen state
this.frozen = data.frozen || false
}
modify(data) {
let rebuild
// Handle frozen state
if (data.hasOwnProperty('frozen')) {
this.frozen = data.frozen
this.data.frozen = data.frozen
if (this.frozen && this.data.mover) {
this.data.mover = null
rebuild = true
}
}
// Block position/rotation updates when frozen
if (data.hasOwnProperty('position') && !this.frozen) {
this.data.position = data.position
this.networkPos.pushArray(data.position)
}
if (data.hasOwnProperty('quaternion') && !this.frozen) {
this.data.quaternion = data.quaternion
this.networkQuat.pushArray(data.quaternion)
}
if (rebuild) {
this.build()
}
}
move() {
// Block movement when frozen
if (this.frozen) return
this.data.mover = this.world.network.id
this.build()
world.network.send('entityModified', {
id: this.data.id,
mover: this.data.mover
})
}
}
```
### Features
- Direct numeric input for position/rotation
- Freeze toggle to prevent movement
- Real-time network sync
- Works with existing movement system
#### Usage
1. Right-click an app to open inspect pane
2. Use numeric inputs for precise positioning
3. Toggle freeze to lock in place
4. Changes sync across network automatically
### Testing
1. Verify position/rotation inputs update in real-time
2. Check freeze prevents all movement
3. Confirm changes sync to other clients
4. Test interaction with right-click move system
## Door
> A powerful HyperScript implementing a door for hyperfy
### 📋 Implementation Overview
### Core State Variables
| State Variable | Purpose |
|---------------|---------|
| `isOpen` | Tracks whether the door is currently open or closed |
| `isMoving` | Indicates if the door is in motion |
| `currentPosition` | Current door position (0 = closed, 1 = open) |
| `targetPosition` | Target door position (0 = closed, 1 = open) |
| `openTimer` | Tracks duration before auto-closing |
### Configuration Options
```json
{
"type": "sliding", // or "swinging"
"direction": "outward", // or "inward"
"slideDistance": 0.65,
"maxRotation": 45
}
```
### 🎮 Door Components
it is important to match the names of the components in your blender scene to the names in the code.
- `doorFrame`: Main door structure
- `doorL`: Left door component
- `doorR`: Right door component

### ⚙️ Configuration UI
#### Available Settings
- **Door Type**
- Sliding (default)
- Swinging
- **Slide Distance**
- Default: 1.8 units
- **Max Rotation**
- Default: 45 degrees
- **Direction**
- Inward
- Outward
### 🔄 Door Types
#### 1. Sliding Doors FUTURE MODE
- Horizontal movement along X-axis
- Configurable slide distance
- Smooth animation system
#### 2. Swinging Doors SALOON MODE
- Rotation-based movement
- Adjustable maximum angle
- Direction control
### 🎯 Interaction System
1. Trigger action appears above door frame
2. Players can toggle door state
3. Action label updates dynamically
4. Auto-close activates after 3 seconds
5. Smooth animation transitions
### 🔧 Technical Notes
- Uses delta time for consistent animations
- Value clamping for stability
- Automatic state management
- Configurable through simple JSON
> **Pro Tip**: All numerical values are automatically clamped to prevent unexpected behavior.
#### 🚪 Setting Up Your Door in Blender
Proper setup in Blender is crucial for this script to function correctly. Follow these steps carefully:
##### 0. 🔍 Figure out the door structure (important)
To achieve the door animation, you need to have a good hierarchy and naming of your rigid body components. In the example object the door is comprised of 4 parts:
- Empty: The parent object of the door.
- FrameRigidBody: The main structure of the door.
- FrameCollider: The collider for the door frame
- FrameMesh: The mesh for the door frame
- LeftDoorRigidBody: The left panel of the door.
- LeftDoorCollider: The collider for the left door.
- LeftDoorMesh: The mesh for the left door.
- RightDoorRigidBody: The right panel of the door.
- RightDoorCollider: The collider for the right door.
- RightDoorMesh: The mesh for the right door.

---
##### 1. 🏗️ Door Frame (Rigid Body / static)
- **Name**: `Frame`
- **Description**: This is the main structure of your door. It should be a static object that serves as the parent for all door components.
- **Positioning**: Place it where you want your door to appear in the environment.
---
##### 2. ⬅️ Create the Left Door (Rigid Body / kinematic)
- **Name**: `LeftDoor`
- **Description**: This is the left panel of your door. It should be a child of the `Empty` object.
---
##### 3. ➡️ Create the Right Door (Rigid Body / kinematic)
- **Name**: `RightDoor`
- **Description**: This is the right panel of your door. It should also be a child of the `Empty` object.
---
##### 4. 💾 Export Your Meshes
- Export your entire setup (Empty, Frame, LeftDoor, RightDoor) as a single .glb file for use in hyperfy.
- Ensure all names match exactly as specified (`Empty`, `Frame`, `LeftDoor`, `RightDoor`).
## Implementing Double Jump in Hyperfy
This guide explains how to add a double jump feature with a flip animation to your Hyperfy world.
### 1. Create the Double Jump System
Create a new file `src/core/systems/DoubleJump.js`:
```javascript
import { System } from './System'
import * as THREE from '../extras/three'
import { Emotes, emotes } from '../extras/playerEmotes'
export class DoubleJump extends System {
constructor(world) {
super(world)
this.lastJumpTime = 0
this.DOUBLE_JUMP_FORCE = 9.75 // 1.5x the base jump force
this.isDoubleJumping = false
}
async init({ loadPhysX }) {
// Preload the flip animation
await this.world.loader.load('emote', emotes[Emotes.DOUBLE_JUMP])
}
start() {
// Bind to space key with priority 0
this.control = this.world.controls.bind({
priority: 0,
onPress: code => this.handleKeyPress(code)
})
}
getLocalPlayer() {
return Array.from(this.world.entities.items.values())
.find(entity => entity.isPlayer && entity.constructor.name === 'PlayerLocal')
}
update() {
const localPlayer = this.getLocalPlayer()
if (!localPlayer) return
// Handle animation timing
if (this.isDoubleJumping) {
const timeSinceLastJump = performance.now() - this.lastJumpTime
if (timeSinceLastJump > 400) {
// Switch back to float after flip completes
localPlayer.emote = Emotes.FLOAT
}
if (timeSinceLastJump > 800) {
this.isDoubleJumping = false
}
}
// Reset on landing
if (localPlayer.grounded) {
this.isDoubleJumping = false
this.lastJumpTime = 0
}
}
handleKeyPress(code) {
if (code !== 'Space') return false
const localPlayer = this.getLocalPlayer()
if (!localPlayer) return false
// Only double jump if:
// 1. In the air (jumping/falling)
// 2. Not already double jumping
// 3. Not grounded
if (!localPlayer.grounded && !this.isDoubleJumping &&
(localPlayer.jumping || localPlayer.falling)) {
// Apply upward force
const currentVel = localPlayer.capsule.getLinearVelocity()
v1.copy(currentVel)
v1.y = this.DOUBLE_JUMP_FORCE
localPlayer.capsule.setLinearVelocity(v1.toPxVec3())
// Start flip animation
this.isDoubleJumping = true
this.lastJumpTime = performance.now()
localPlayer.emote = Emotes.DOUBLE_JUMP
// Sync animation with other players
this.world.network.send('entityModified', {
id: localPlayer.data.id,
p: localPlayer.base.position.toArray(),
q: localPlayer.base.quaternion.toArray(),
e: Emotes.DOUBLE_JUMP,
})
return true
}
return false
}
}
```
### 2. Add the Double Jump Emote
Add the following to `src/core/extras/playerEmotes.js`:
```javascript
export const Emotes = {
IDLE: 0,
WALK: 1,
RUN: 2,
FLOAT: 3,
DOUBLE_JUMP: 4, // Add this line
// ... other emotes
}
export const emotes = {
0: 'asset://emote-idle.glb',
1: 'asset://emote-walk.glb',
2: 'asset://emote-run.glb',
3: 'asset://emote-float.glb',
4: 'asset://emote-flip.glb', // Add this line
// ... other emotes
}
```
### 3. Register the System
Add this to your world creation (usually in `createClientWorld.js`):
```javascript
import { DoubleJump } from './systems/DoubleJump'
// In your world creation:
world.register(DoubleJump)
```
### Features
The double jump system provides:
- Double jump ability by pressing space while in the air
- Front flip animation during the double jump
- Network synchronization for multiplayer
- Automatic transition back to floating animation
- State reset upon landing
### Technical Details
- Double jump force is 1.5x the normal jump force (9.75)
- Flip animation plays for 400ms before transitioning to float
- Double jump state is tracked for 800ms to prevent additional jumps
- Uses the existing player physics and animation systems
- Integrates with the network layer for multiplayer support
## Dodge System Documentation
### Overview
The Dodge system is an advanced movement mechanic that allows players to perform both ground and aerial dodges. It seamlessly integrates with the existing movement systems, including double jumps, and provides fluid, momentum-based movement options for enhanced player mobility.
### Features
#### Ground Dodges
- **Forward Roll**: Performed while moving
- Maintains directional momentum
- Full dodge force (15 units)
- 30% momentum preservation
- Uses `emote-roll.glb` animation
- **Backstep**: Performed while stationary
- Moves backward relative to player orientation
- Full dodge force (15 units)
- 30% momentum preservation
- Uses `emote-backstep.glb` animation
#### Air Dodges
The air dodge system features dynamic force adjustments based on the player's jump state:
1. **After First Jump**
- 80% of base dodge force
- 50% momentum preservation
- 0.3 units of upward force
- Directional control based on movement input
2. **After Double Jump**
- 70% of base dodge force
- 60% momentum preservation
- 0.2 units of upward force
- Enhanced momentum preservation for better control
3. **During Fall**
- 75% of base dodge force
- 50% momentum preservation
- 0.3 units of upward force
- Direction based on movement or forward orientation
### Technical Details
#### System Properties
```javascript
dodgeForce = 15 // Base force for all dodges
dodgeDuration = 0.7 // Duration of dodge animation in seconds
dodgeCooldown = 2000 // Cooldown between dodges in milliseconds
```
#### Movement Mechanics
#### Force Calculation
```javascript
finalForce = baseDodgeForce * stateMultiplier
```
Where `stateMultiplier` is:
- Ground: 1.0
- Air (first jump): 0.8
- Air (double jump): 0.7
- Air (falling): 0.75
#### Momentum Preservation
```javascript
momentumFactor = {
ground: 0.3,
air: 0.5,
doubleJump: 0.6
}
```
#### Upward Force (Air Dodges)
```javascript
upwardForce = {
normal: 0.3,
afterDoubleJump: 0.2
}
```
### Integration with Other Systems
#### Double Jump Integration
- Cannot dodge during double jump animation
- Reduced forces after double jump
- Enhanced momentum preservation for better control
- Reduced upward force to prevent excessive height gain
#### Physics Integration
- Uses PhysX capsule for collision detection
- Maintains momentum through velocity preservation
- Applies forces relative to player orientation
- Handles ground and air states separately
#### Animation System
- Seamlessly transitions between dodge and movement animations
- Respects animation priority with double jump system
- Returns to appropriate idle/movement state after dodge
- Network synchronized for multiplayer consistency
### Usage Examples
#### Basic Usage
```javascript
// Ground dodge while running forward
W + SHIFT = Forward roll
// Ground dodge while stationary
SHIFT = Backstep
// Air dodge while moving
JUMP + (direction) + SHIFT = Directional air dodge
// Air dodge after double jump
JUMP + JUMP + SHIFT = Reduced-force air dodge
```
#### Advanced Techniques
1. **Jump-Cancel Dodge**
- Jump during a ground dodge
- Maintains dodge momentum into the jump
- Can chain into double jump or air dodge
2. **Momentum-Preserved Air Dodge**
- Use movement momentum before air dodge
- Higher preservation factor in air
- Useful for covering large distances
3. **Double Jump to Air Dodge**
- Double jump for height
- Air dodge for horizontal distance
- Uses reduced force but higher momentum preservation
### Network Synchronization
The system sends the following data for multiplayer synchronization:
```javascript
{
id: player.id,
p: position.toArray(),
q: quaternion.toArray(),
e: emoteType
}
```
### Performance Considerations
- Efficient use of vector pooling (v1) for calculations
- Minimal garbage collection impact
- Optimized physics calculations
- Smart animation state management
### Debug Information
The system includes extensive debug logging:
- Dodge state and progress
- Force calculations
- Movement state
- Animation transitions
- Physics velocity updates
### Future Considerations
Potential areas for enhancement:
1. Additional dodge types (side dodges, recovery dodges)
2. Customizable dodge parameters per player
3. Enhanced VFX/SFX integration
4. Advanced combo system integration
5. Dodge canceling mechanics
### Dependencies
- Three.js for vector math and quaternions
- PhysX for physics simulation
- Custom animation system for emotes
- Network synchronization system
- Player control system
### Files
- `src/core/systems/Dodge.js` - Main system implementation
- `src/core/extras/playerEmotes.js` - Emote definitions
- `assets/emote-roll.glb` - Roll animation
- `assets/emote-backstep.glb` - Backstep animation
emote-roll.glb can be found in the hyperfy-ref
emote-backstep.glb can be found in the hyperfy-ref
## Sky Controller Documentation (concept)
### Overview
The Sky Controller is a Hyperfy script that allows dynamic control of the sky and environmental lighting in your world.
It provides a user-friendly interface to switch between different times of day and customize each state with specific sky textures and HDR environment maps.
### Features
- Four different time-of-day states: Day (☀️), Dusk (🌅), Night (🌙), and Aurora (🌌)
- Custom sky texture support for each state
- Custom HDR lighting support for each state
- Real-time switching between states
- Default engine sky fallback for day mode
### Technical Implementation
#### Configuration UI
```javascript
app.configure(() => {
return [
// Section Header
{
type: 'section',
key: 'title',
label: 'Sky',
},
// Time of Day Switcher
{
type: 'switch',
key: 'switch',
label: 'TOD',
value: 1,
options: [
{ value: 1, label: '☀️' },
{ value: 2, label: '🌅' },
{ value: 3, label: '🌙' },
{ value: 4, label: '🌌' }
]
},
// File inputs for each state...
]
})
```
The configuration UI is built using Hyperfy's configuration system, which automatically creates an inspector panel with:
- A section header labeled "Sky"
- A switch control for Time of Day (TOD)
- File upload fields for each sky texture and HDR map
#### Sky States
Each state (except Day) requires two files:
1. **Sky Texture**: A background image for the sky dome
- File type: JPG or PNG
- Purpose: Visual appearance of the sky
2. **HDR Map**: A high dynamic range environment map
- File type: HDR
- Purpose: Environmental lighting and reflections
#### State Configurations:
1. **Day Mode** (☀️)
- Uses engine defaults
- No files required
- Clean, default lighting
2. **Dusk Mode** (🌅)
- `sky1`: Dusk sky texture
- `hdr1`: Dusk HDR lighting
3. **Night Mode** (🌙)
- `sky2`: Night sky texture
- `hdr2`: Night HDR lighting
4. **Aurora Mode** (🌌)
- `sky3`: Aurora sky texture
- `hdr3`: Aurora HDR lighting
#### Core Functionality
#### Sky Creation
```javascript
const sky = app.create('sky')
app.add(sky)
```
Creates and adds a sky entity to the world.
#### Update Function
```javascript
function updateSky() {
const mode = app.config.switch
console.log('Current mode:', mode)
if (mode === 4) {
// Aurora settings
} else if (mode === 3) {
// Night settings
} else if (mode === 2) {
// Dusk settings
} else {
// Day settings
}
}
```
The `updateSky()` function:
- Reads the current mode from configuration
- Sets appropriate sky texture (`sky.bg`) and HDR map (`sky.hdr`)
- Falls back to engine defaults for day mode
#### Event Handling
```javascript
// Initial setup
updateSky()
// Listen for configuration changes
app.on('config', () => {
updateSky()
})
// Update every frame to ensure sync
app.on('update', dt => {
updateSky()
})
```
The script maintains synchronization through:
1. Initial state setup on load
2. Configuration change listener for UI interactions
3. Frame update listener for continuous synchronization
### Usage Instructions
1. **Basic Setup**:
- Add the script to your world
- Scale appropriately using `app.scale.set(2.5, 2.5, 1.5)`
2. **Customizing States**:
- Click the file upload fields in the inspector
- Select appropriate sky textures (JPG/PNG) and HDR maps
- Files are automatically uploaded and cached
3. **Switching States**:
- Use the TOD switch in the inspector
- Changes take effect immediately
- Switch between Day, Dusk, Night, and Aurora modes
4. **Best Practices**:
- Use appropriately sized textures for performance
- Ensure HDR maps match their corresponding sky textures
- Test transitions between states for smooth changes
### Technical Notes
- The script uses optional chaining (`?.`) for safe property access
- Configuration updates are handled through Hyperfy's event system
- Frame updates ensure consistent state synchronization
- File uploads are managed by Hyperfy's asset system
# ashxn
## UV Scroll on Contact
```
const body = app.get('Body')
const mesh = app.get('Mesh')
let active = false
body.onContactStart = e => {
console.log('start', e.player)
if (e.player) {
active = true
}
}
body.onContactEnd = e => {
console.log('end', e.player)
if (e.player) {
active = false
}
}
app.on('update', delta => {
if (!active) return
mesh.material.textureX += 1 * delta
mesh.material.textureY += 1 * delta
})
```
# devilsadvocate.sol
## Getting Started With Hyperfy V2
Begin by dragging a glb into your hyperfy world. Remember to give yourself admin priviledges with `/admin <admin-key>`. Right click on the model to inspect it and click script to open the in world scripting UI.
```
const appId = app.id
console.log('appId: ', appId)
```
When you drag a model into a hyperfy world an app entity is automatically created with an appId matching the root model name. If you don't know this name, you can return it with app.id.
```
const model = app.get(app.id)
console.log('model',model)
```
## World Hierarchy
```
Worlds
└─ Entities
├─ Players
└─ Apps
└─ rootNodes (root model)
└─ subNodes (sub-model e.g., collider)
```
A node or its subnodes can be accessed using app.get('node-id'). This is useful for assigning colliders, rigidbodies, or actions to specific subnodes of a root model.
const rb = app.create('rigidbody')
rb.type = 'dynamic'
## Rigidbody Types
To allow your model to interact with physics it needs a rigidbody. There are three types: static(default), dynamic, or kinematic. Use the latter two options if you plan to move the object through code, with kinematic being most performant(clarity need on why this is). Other rigidbody attributes can be found in the github docs.
```
const collider = app.create('collider')
rb.add(collider)
app.add(rb)
```
Once we've created our rigidbody we can assign a collider to it. After creating the rb and adding the collider to it, we can add the rb to the app runtime making our object collidable. Colliders can also be returned from a model object using app.get('collider-name') if they have been included in the glb model as subnodes.
```
const action = app.create('action')
action.label = 'Water Plant'
action.position.set(0, 2.2, 0)
action.duration = 0.3
```
## Actions
Another app attribute are actions which can be used to make objects interactable in the world. Actions include attributes like a label, position, and duration to control their appearance and function.
```
action.onTrigger= () => {
console.log(appId)
}
app.add(action)
```
Additionally actions can trigger functions when they are interacted with by a player. Actions must be added directly to the app with app.add(action) or to their nodes/subnodes using app.get('node-to-add-action').add(action)
```
const appPosition = app.position
console.log('app', app)
console.log('action', action)
console.log('rb', rb)
console.log('collider', collider)
```