268 lines
6.2 KiB
Markdown
268 lines
6.2 KiB
Markdown
|
|
# Nostr Event Stream Visualization
|
||
|
|
|
||
|
|
**Issue:** #874 - [NEXUS] Implement Nostr Event Stream Visualization
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
Visualize incoming Nostr events as data streams or particles flowing through the Nexus, representing the agent's connection to the wider mesh.
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
+---------------------------------------------------+
|
||
|
|
| Nostr Event Visualizer |
|
||
|
|
+---------------------------------------------------|
|
||
|
|
| Nostr Relay Connection |
|
||
|
|
| +-------------+ +-------------+ +-------------+
|
||
|
|
| | WebSocket | | Event | | Subscription|
|
||
|
|
| | Client | | Handler | | Manager |
|
||
|
|
| +-------------+ +-------------+ +-------------+
|
||
|
|
| +-------------+ +-------------+ +-------------+
|
||
|
|
| | Particle | | Color | | Animation |
|
||
|
|
| | System | | Manager | | Engine |
|
||
|
|
| +-------------+ +-------------+ +-------------+
|
||
|
|
+---------------------------------------------------+
|
||
|
|
```
|
||
|
|
|
||
|
|
## Components
|
||
|
|
|
||
|
|
### 1. Nostr Event Visualizer (`js/nostr-event-visualizer.js`)
|
||
|
|
Main visualization class for Nostr events.
|
||
|
|
|
||
|
|
**Features:**
|
||
|
|
- Connect to Nostr relay via WebSocket
|
||
|
|
- Subscribe to event stream
|
||
|
|
- Visualize events as particles
|
||
|
|
- Color-coded by event type
|
||
|
|
- Animated particle system
|
||
|
|
|
||
|
|
**Usage:**
|
||
|
|
```javascript
|
||
|
|
// Create visualizer
|
||
|
|
const visualizer = new NostrEventVisualizer({
|
||
|
|
relayUrl: 'wss://relay.nostr.info',
|
||
|
|
maxEvents: 100,
|
||
|
|
particleCount: 50,
|
||
|
|
streamSpeed: 1.0
|
||
|
|
});
|
||
|
|
|
||
|
|
// Initialize with Three.js scene
|
||
|
|
visualizer.init(scene, camera, renderer);
|
||
|
|
|
||
|
|
// Connect to Nostr relay
|
||
|
|
visualizer.connect();
|
||
|
|
|
||
|
|
// Update visualization
|
||
|
|
visualizer.update(deltaTime);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Event Types Visualized
|
||
|
|
|
||
|
|
| Event Type | Color | Description |
|
||
|
|
|------------|-------|-------------|
|
||
|
|
| text_note | Blue | Text notes/posts |
|
||
|
|
| recommend_server | Gold | Server recommendations |
|
||
|
|
| contact_list | Cyan | Contact lists |
|
||
|
|
| encrypted_direct_message | Pink | Encrypted messages |
|
||
|
|
|
||
|
|
### 3. Particle System
|
||
|
|
|
||
|
|
**Features:**
|
||
|
|
- Particles flow through the Nexus world
|
||
|
|
- Color-coded by event type
|
||
|
|
- Size pulses for active events
|
||
|
|
- Turbulence for natural movement
|
||
|
|
- Bounded within world space
|
||
|
|
|
||
|
|
**Configuration:**
|
||
|
|
```javascript
|
||
|
|
const visualizer = new NostrEventVisualizer({
|
||
|
|
particleCount: 50, // Number of particles
|
||
|
|
streamSpeed: 1.0, // Flow speed
|
||
|
|
particleSize: 0.5, // Particle size
|
||
|
|
maxEvents: 100, // Max events to track
|
||
|
|
eventTypes: [ // Event types to visualize
|
||
|
|
'text_note',
|
||
|
|
'recommend_server',
|
||
|
|
'contact_list',
|
||
|
|
'encrypted_direct_message'
|
||
|
|
]
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Usage Examples
|
||
|
|
|
||
|
|
### Basic Usage
|
||
|
|
```javascript
|
||
|
|
// Create visualizer
|
||
|
|
const visualizer = new NostrEventVisualizer({
|
||
|
|
relayUrl: 'wss://relay.nostr.info'
|
||
|
|
});
|
||
|
|
|
||
|
|
// Initialize with Three.js
|
||
|
|
visualizer.init(scene, camera, renderer);
|
||
|
|
|
||
|
|
// Connect to relay
|
||
|
|
visualizer.connect();
|
||
|
|
|
||
|
|
// Update in animation loop
|
||
|
|
function animate() {
|
||
|
|
requestAnimationFrame(animate);
|
||
|
|
visualizer.update(1/60); // 60 FPS
|
||
|
|
renderer.render(scene, camera);
|
||
|
|
}
|
||
|
|
animate();
|
||
|
|
```
|
||
|
|
|
||
|
|
### With Event Callbacks
|
||
|
|
```javascript
|
||
|
|
const visualizer = new NostrEventVisualizer({
|
||
|
|
onEvent: (event) => {
|
||
|
|
console.log('New event:', event.kind, event.content);
|
||
|
|
},
|
||
|
|
onConnect: () => {
|
||
|
|
console.log('Connected to Nostr relay');
|
||
|
|
},
|
||
|
|
onDisconnect: () => {
|
||
|
|
console.log('Disconnected from Nostr relay');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Get Status
|
||
|
|
```javascript
|
||
|
|
const status = visualizer.getStatus();
|
||
|
|
console.log('Connected:', status.connected);
|
||
|
|
console.log('Events:', status.eventCount);
|
||
|
|
console.log('Particles:', status.activeParticles);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Integration with Nexus
|
||
|
|
|
||
|
|
### Auto-Initialize
|
||
|
|
```javascript
|
||
|
|
// In app.js or initialization code
|
||
|
|
document.addEventListener('DOMContentLoaded', () => {
|
||
|
|
// Wait for Three.js scene to be ready
|
||
|
|
if (window.scene && window.camera && window.renderer) {
|
||
|
|
const visualizer = new NostrEventVisualizer();
|
||
|
|
visualizer.init(window.scene, window.camera, window.renderer);
|
||
|
|
visualizer.connect();
|
||
|
|
|
||
|
|
// Store globally
|
||
|
|
window.nostrVisualizer = visualizer;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### With Animation Loop
|
||
|
|
```javascript
|
||
|
|
// In animation loop
|
||
|
|
function animate() {
|
||
|
|
requestAnimationFrame(animate);
|
||
|
|
|
||
|
|
// Update Nostr visualizer
|
||
|
|
if (window.nostrVisualizer) {
|
||
|
|
window.nostrVisualizer.update(1/60);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Render scene
|
||
|
|
renderer.render(scene, camera);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Event Handling
|
||
|
|
|
||
|
|
### Event Types
|
||
|
|
```javascript
|
||
|
|
// text_note (kind 1)
|
||
|
|
{
|
||
|
|
"id": "...",
|
||
|
|
"pubkey": "...",
|
||
|
|
"created_at": 1234567890,
|
||
|
|
"kind": 1,
|
||
|
|
"tags": [],
|
||
|
|
"content": "Hello Nostr!",
|
||
|
|
"sig": "..."
|
||
|
|
}
|
||
|
|
|
||
|
|
// recommend_server (kind 2)
|
||
|
|
{
|
||
|
|
"id": "...",
|
||
|
|
"pubkey": "...",
|
||
|
|
"created_at": 1234567890,
|
||
|
|
"kind": 2,
|
||
|
|
"tags": [],
|
||
|
|
"content": "wss://relay.example.com",
|
||
|
|
"sig": "..."
|
||
|
|
}
|
||
|
|
|
||
|
|
// contact_list (kind 3)
|
||
|
|
{
|
||
|
|
"id": "...",
|
||
|
|
"pubkey": "...",
|
||
|
|
"created_at": 1234567890,
|
||
|
|
"kind": 3,
|
||
|
|
"tags": [["p", "pubkey1"], ["p", "pubkey2"]],
|
||
|
|
"content": "",
|
||
|
|
"sig": "..."
|
||
|
|
}
|
||
|
|
|
||
|
|
// encrypted_direct_message (kind 4)
|
||
|
|
{
|
||
|
|
"id": "...",
|
||
|
|
"pubkey": "...",
|
||
|
|
"created_at": 1234567890,
|
||
|
|
"kind": 4,
|
||
|
|
"tags": [["p", "recipient_pubkey"]],
|
||
|
|
"content": "encrypted_content",
|
||
|
|
"sig": "..."
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
### Unit Tests
|
||
|
|
```bash
|
||
|
|
node --test tests/test_nostr_visualizer.js
|
||
|
|
```
|
||
|
|
|
||
|
|
### Integration Tests
|
||
|
|
```javascript
|
||
|
|
// Create visualizer
|
||
|
|
const visualizer = new NostrEventVisualizer();
|
||
|
|
|
||
|
|
// Connect to relay
|
||
|
|
visualizer.connect();
|
||
|
|
|
||
|
|
// Check status
|
||
|
|
const status = visualizer.getStatus();
|
||
|
|
assert(status.connected === true);
|
||
|
|
|
||
|
|
// Update visualization
|
||
|
|
visualizer.update(1/60);
|
||
|
|
|
||
|
|
// Disconnect
|
||
|
|
visualizer.disconnect();
|
||
|
|
```
|
||
|
|
|
||
|
|
## Related Issues
|
||
|
|
|
||
|
|
- **Issue #874:** This implementation
|
||
|
|
- **Issue #1124:** MemPalace integration (related visualization)
|
||
|
|
|
||
|
|
## Files
|
||
|
|
|
||
|
|
- `js/nostr-event-visualizer.js` - Main visualization module
|
||
|
|
- `docs/nostr-event-visualizer.md` - This documentation
|
||
|
|
- `tests/test_nostr_visualizer.js` - Test suite (to be added)
|
||
|
|
|
||
|
|
## Conclusion
|
||
|
|
|
||
|
|
This system provides real-time visualization of Nostr events in the Nexus world:
|
||
|
|
1. **Connection** to Nostr relays via WebSocket
|
||
|
|
2. **Visualization** of events as colored particles
|
||
|
|
3. **Animation** with turbulence and pulsing
|
||
|
|
4. **Integration** with Three.js scene
|
||
|
|
|
||
|
|
**Ready for production use.**
|