@hypertools/sdk 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -0
- package/examples/README.md +84 -0
- package/examples/p5js/index.html +39 -0
- package/examples/p5js/main.ts +136 -0
- package/examples/react/index.html +22 -0
- package/examples/react/main.tsx +310 -0
- package/examples/react-landing/README.md +64 -0
- package/examples/react-landing/index.html +14 -0
- package/examples/react-landing/package.json +23 -0
- package/examples/react-landing/public/boids-flocking-project.js +12 -0
- package/examples/react-landing/src/App.css +379 -0
- package/examples/react-landing/src/App.tsx +483 -0
- package/examples/react-landing/src/main.tsx +9 -0
- package/examples/react-landing/src/types.d.ts +24 -0
- package/examples/react-landing/tsconfig.json +20 -0
- package/examples/react-landing/vite.config.ts +9 -0
- package/examples/recording/index.html +113 -0
- package/examples/recording/main.ts +256 -0
- package/examples/threejs/index.html +40 -0
- package/examples/threejs/main.ts +196 -0
- package/examples/vanilla-canvas/index.html +77 -0
- package/examples/vanilla-canvas/main.ts +162 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -402,10 +402,185 @@ controls.refresh();
|
|
|
402
402
|
controls.dispose();
|
|
403
403
|
```
|
|
404
404
|
|
|
405
|
+
## Using Exported HyperTools Experiences
|
|
406
|
+
|
|
407
|
+
When you export a project from [HyperTools](https://hypertools.dev), you get a standalone web component that can be embedded anywhere. Use `ExperienceController` from this SDK to control it programmatically.
|
|
408
|
+
|
|
409
|
+
### Setup
|
|
410
|
+
|
|
411
|
+
1. **Get your exported experience** - Export from HyperTools to get a JS file (e.g., `my-experience.js`)
|
|
412
|
+
|
|
413
|
+
2. **Load the experience** in your HTML:
|
|
414
|
+
```html
|
|
415
|
+
<script src="./my-experience.js"></script>
|
|
416
|
+
<my-experience></my-experience>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
3. **Install the SDK** to control it:
|
|
420
|
+
```bash
|
|
421
|
+
npm install @hypertools/sdk
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Basic Control
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import { ExperienceController } from '@hypertools/sdk';
|
|
428
|
+
|
|
429
|
+
// Get reference to the web component
|
|
430
|
+
const element = document.querySelector('my-experience');
|
|
431
|
+
|
|
432
|
+
// Connect the controller
|
|
433
|
+
const controller = new ExperienceController({
|
|
434
|
+
element,
|
|
435
|
+
initialParams: {
|
|
436
|
+
speed: 5,
|
|
437
|
+
color: '#ff0000',
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Control parameters
|
|
442
|
+
controller.setParam('speed', 10);
|
|
443
|
+
controller.setParams({ speed: 8, color: '#00ff00' });
|
|
444
|
+
|
|
445
|
+
// Get current values
|
|
446
|
+
const params = controller.getParams();
|
|
447
|
+
const paramDefs = controller.getParamDefs();
|
|
448
|
+
|
|
449
|
+
// Reset to defaults
|
|
450
|
+
controller.resetParams();
|
|
451
|
+
|
|
452
|
+
// Listen to changes
|
|
453
|
+
controller.on('paramChange', (event) => {
|
|
454
|
+
console.log(`${event.key} changed to ${event.value}`);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// Cleanup when done
|
|
458
|
+
controller.destroy();
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Dispatching Events
|
|
462
|
+
|
|
463
|
+
Trigger interactions programmatically by dispatching synthetic events:
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
// Simulate a click
|
|
467
|
+
controller.dispatchToCanvas('click', { clientX: 400, clientY: 300 });
|
|
468
|
+
|
|
469
|
+
// Simulate a long press (e.g., for formation triggers)
|
|
470
|
+
controller.dispatchToCanvas('mousedown', { clientX: 400, clientY: 300 });
|
|
471
|
+
setTimeout(() => {
|
|
472
|
+
controller.dispatchToCanvas('mouseup', { clientX: 400, clientY: 300 });
|
|
473
|
+
}, 600);
|
|
474
|
+
|
|
475
|
+
// Keyboard events
|
|
476
|
+
controller.dispatchToCanvas('keydown', { key: 'Space' });
|
|
477
|
+
|
|
478
|
+
// Custom events
|
|
479
|
+
controller.dispatchCustomEvent('myEvent', { data: 'hello' });
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### React Integration
|
|
483
|
+
|
|
484
|
+
```tsx
|
|
485
|
+
import { useEffect, useRef, useState } from 'react';
|
|
486
|
+
import { ExperienceController } from '@hypertools/sdk';
|
|
487
|
+
import type { ExportedExperienceElement } from '@hypertools/sdk';
|
|
488
|
+
|
|
489
|
+
function App() {
|
|
490
|
+
const experienceRef = useRef<ExportedExperienceElement>(null);
|
|
491
|
+
const controllerRef = useRef<ExperienceController | null>(null);
|
|
492
|
+
const [isReady, setIsReady] = useState(false);
|
|
493
|
+
|
|
494
|
+
useEffect(() => {
|
|
495
|
+
const element = experienceRef.current;
|
|
496
|
+
if (!element) return;
|
|
497
|
+
|
|
498
|
+
const onReady = () => {
|
|
499
|
+
controllerRef.current = new ExperienceController({ element });
|
|
500
|
+
setIsReady(true);
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
element.addEventListener('ready', onReady, { once: true });
|
|
504
|
+
|
|
505
|
+
return () => {
|
|
506
|
+
element.removeEventListener('ready', onReady);
|
|
507
|
+
controllerRef.current?.destroy();
|
|
508
|
+
};
|
|
509
|
+
}, []);
|
|
510
|
+
|
|
511
|
+
return (
|
|
512
|
+
<div>
|
|
513
|
+
{/* @ts-expect-error - Custom element */}
|
|
514
|
+
<my-experience ref={experienceRef} />
|
|
515
|
+
|
|
516
|
+
{isReady && (
|
|
517
|
+
<button onClick={() => controllerRef.current?.setParam('speed', 10)}>
|
|
518
|
+
Speed Up
|
|
519
|
+
</button>
|
|
520
|
+
)}
|
|
521
|
+
</div>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### ExperienceController API
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// Constructor options
|
|
530
|
+
interface ExperienceControllerConfig {
|
|
531
|
+
element: ExportedExperienceElement; // The web component
|
|
532
|
+
initialParams?: Record<string, unknown>; // Override initial values
|
|
533
|
+
autoConnect?: boolean; // Connect immediately (default: true)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Methods
|
|
537
|
+
controller.connect(); // Connect to element
|
|
538
|
+
controller.disconnect(); // Disconnect from element
|
|
539
|
+
controller.destroy(); // Full cleanup
|
|
540
|
+
|
|
541
|
+
controller.setParam(key, value);
|
|
542
|
+
controller.setParams(params);
|
|
543
|
+
controller.getParams();
|
|
544
|
+
controller.getParamDefs();
|
|
545
|
+
controller.resetParams();
|
|
546
|
+
|
|
547
|
+
controller.on(event, handler); // Subscribe
|
|
548
|
+
controller.once(event, handler); // Subscribe once
|
|
549
|
+
controller.off(event, handler); // Unsubscribe
|
|
550
|
+
|
|
551
|
+
controller.dispatchToCanvas(eventType, eventInit); // Dispatch to canvas
|
|
552
|
+
controller.dispatchToElement(eventType, eventInit); // Dispatch to element
|
|
553
|
+
controller.dispatchCustomEvent(type, detail); // Custom event
|
|
554
|
+
|
|
555
|
+
controller.getCanvas(); // Get canvas element
|
|
556
|
+
controller.getMount(); // Get mount element
|
|
557
|
+
|
|
558
|
+
// Properties
|
|
559
|
+
controller.element; // The web component
|
|
560
|
+
controller.isConnected; // Connection status
|
|
561
|
+
controller.isDestroyed; // Destroyed status
|
|
562
|
+
|
|
563
|
+
// Static factories
|
|
564
|
+
ExperienceController.fromSelector('my-experience');
|
|
565
|
+
await ExperienceController.whenDefined('my-experience');
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### Building Custom Features
|
|
569
|
+
|
|
570
|
+
See [`examples/react-landing/`](./examples/react-landing/) for a complete example showing how to build custom features on top of exported experiences:
|
|
571
|
+
|
|
572
|
+
- **Preset System** - Pre-configured settings with visual selection
|
|
573
|
+
- **Click-to-Form Mode** - Click anywhere to trigger interactions at that position
|
|
574
|
+
- **Auto-pilot Mode** - Automatically cycle through presets
|
|
575
|
+
- **Idle Screensaver** - Start animations after inactivity
|
|
576
|
+
- **Share Configuration** - Generate shareable URLs with encoded settings
|
|
577
|
+
- **Keyboard Shortcuts** - Custom keyboard controls
|
|
578
|
+
|
|
405
579
|
## Examples
|
|
406
580
|
|
|
407
581
|
See the `/examples` directory for complete working examples:
|
|
408
582
|
|
|
583
|
+
- `examples/react-landing/` - **React app with exported experience as background + custom features**
|
|
409
584
|
- `examples/vanilla-canvas/` - Pure Canvas API
|
|
410
585
|
- `examples/p5js/` - p5.js sketch
|
|
411
586
|
- `examples/threejs/` - Three.js scene
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @hypertools/sdk Examples
|
|
2
|
+
|
|
3
|
+
Working examples demonstrating different SDK features and integrations.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
| Example | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| [vanilla-canvas](./vanilla-canvas/) | Pure Canvas API with particle system |
|
|
10
|
+
| [p5js](./p5js/) | p5.js generative art integration |
|
|
11
|
+
| [threejs](./threejs/) | Three.js 3D scene with particles |
|
|
12
|
+
| [react](./react/) | React hooks integration |
|
|
13
|
+
| [recording](./recording/) | Video capture & timeline animation |
|
|
14
|
+
|
|
15
|
+
## Running Examples
|
|
16
|
+
|
|
17
|
+
These examples use TypeScript and require a bundler. You can run them with:
|
|
18
|
+
|
|
19
|
+
### Using Vite (recommended)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install vite
|
|
23
|
+
npm install -g vite
|
|
24
|
+
|
|
25
|
+
# Run an example
|
|
26
|
+
cd examples/vanilla-canvas
|
|
27
|
+
vite
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Using esbuild + serve
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install dependencies
|
|
34
|
+
npm install -g esbuild serve
|
|
35
|
+
|
|
36
|
+
# Build and serve
|
|
37
|
+
cd examples/vanilla-canvas
|
|
38
|
+
esbuild main.ts --bundle --outfile=main.js --format=esm
|
|
39
|
+
serve .
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Using Bun
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd examples/vanilla-canvas
|
|
46
|
+
bun run --bun vite
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Example Structure
|
|
50
|
+
|
|
51
|
+
Each example contains:
|
|
52
|
+
|
|
53
|
+
- `index.html` - HTML entry point
|
|
54
|
+
- `main.ts` or `main.tsx` - TypeScript source code
|
|
55
|
+
|
|
56
|
+
## Dependencies
|
|
57
|
+
|
|
58
|
+
Some examples require peer dependencies:
|
|
59
|
+
|
|
60
|
+
- **p5js**: Requires p5.js (loaded via CDN in example)
|
|
61
|
+
- **threejs**: Requires `three` package
|
|
62
|
+
- **react**: Requires `react` and `react-dom`
|
|
63
|
+
|
|
64
|
+
## Creating Your Own
|
|
65
|
+
|
|
66
|
+
Use these examples as templates for your own experiences:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { Experience } from '@hypertools/sdk';
|
|
70
|
+
|
|
71
|
+
new Experience({
|
|
72
|
+
mount: document.getElementById('container')!,
|
|
73
|
+
paramDefs: {
|
|
74
|
+
// Define your parameters
|
|
75
|
+
},
|
|
76
|
+
setup(context) {
|
|
77
|
+
// Your creative code here
|
|
78
|
+
|
|
79
|
+
return () => {
|
|
80
|
+
// Cleanup
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>HyperTool SDK - p5.js Example</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body {
|
|
10
|
+
font-family: system-ui, sans-serif;
|
|
11
|
+
background: #0f0f1a;
|
|
12
|
+
color: #fff;
|
|
13
|
+
min-height: 100vh;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
align-items: center;
|
|
17
|
+
padding: 2rem;
|
|
18
|
+
}
|
|
19
|
+
h1 { margin-bottom: 1rem; }
|
|
20
|
+
#sketch-container {
|
|
21
|
+
border-radius: 8px;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
}
|
|
24
|
+
.info {
|
|
25
|
+
margin-top: 1rem;
|
|
26
|
+
font-size: 0.9rem;
|
|
27
|
+
color: #888;
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<h1>p5.js Generative Art</h1>
|
|
33
|
+
<div id="sketch-container"></div>
|
|
34
|
+
<p class="info">Move your mouse to influence the pattern. Parameters are reactive!</p>
|
|
35
|
+
|
|
36
|
+
<script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.min.js"></script>
|
|
37
|
+
<script type="module" src="./main.ts"></script>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* p5.js Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates @hypertools/sdk integration with p5.js.
|
|
5
|
+
* Creates a generative art piece with reactive parameters.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Experience } from '@hypertools/sdk';
|
|
9
|
+
import type p5 from 'p5';
|
|
10
|
+
|
|
11
|
+
// Declare p5 as global (loaded via CDN)
|
|
12
|
+
declare const p5: typeof import('p5');
|
|
13
|
+
|
|
14
|
+
new Experience({
|
|
15
|
+
mount: document.getElementById('sketch-container')!,
|
|
16
|
+
paramDefs: {
|
|
17
|
+
backgroundColor: {
|
|
18
|
+
type: 'color',
|
|
19
|
+
value: '#0f0f1a',
|
|
20
|
+
label: 'Background',
|
|
21
|
+
},
|
|
22
|
+
primaryColor: {
|
|
23
|
+
type: 'color',
|
|
24
|
+
value: '#ff6b6b',
|
|
25
|
+
label: 'Primary Color',
|
|
26
|
+
},
|
|
27
|
+
secondaryColor: {
|
|
28
|
+
type: 'color',
|
|
29
|
+
value: '#4ecdc4',
|
|
30
|
+
label: 'Secondary Color',
|
|
31
|
+
},
|
|
32
|
+
shapeCount: {
|
|
33
|
+
type: 'number',
|
|
34
|
+
value: 50,
|
|
35
|
+
min: 10,
|
|
36
|
+
max: 200,
|
|
37
|
+
step: 10,
|
|
38
|
+
label: 'Shape Count',
|
|
39
|
+
},
|
|
40
|
+
noiseScale: {
|
|
41
|
+
type: 'number',
|
|
42
|
+
value: 0.01,
|
|
43
|
+
min: 0.001,
|
|
44
|
+
max: 0.05,
|
|
45
|
+
step: 0.001,
|
|
46
|
+
label: 'Noise Scale',
|
|
47
|
+
},
|
|
48
|
+
rotationSpeed: {
|
|
49
|
+
type: 'number',
|
|
50
|
+
value: 0.5,
|
|
51
|
+
min: 0,
|
|
52
|
+
max: 2,
|
|
53
|
+
step: 0.1,
|
|
54
|
+
label: 'Rotation Speed',
|
|
55
|
+
},
|
|
56
|
+
animate: {
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
value: true,
|
|
59
|
+
label: 'Animate',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
setup(context) {
|
|
63
|
+
let sketch: p5;
|
|
64
|
+
let time = 0;
|
|
65
|
+
|
|
66
|
+
new p5((p: p5) => {
|
|
67
|
+
sketch = p;
|
|
68
|
+
|
|
69
|
+
p.setup = () => {
|
|
70
|
+
p.createCanvas(800, 600);
|
|
71
|
+
p.colorMode(p.HSB, 360, 100, 100, 100);
|
|
72
|
+
p.noStroke();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
p.draw = () => {
|
|
76
|
+
if (!context.params.animate) return;
|
|
77
|
+
|
|
78
|
+
// Background with slight transparency for trails
|
|
79
|
+
p.background(context.params.backgroundColor as string);
|
|
80
|
+
|
|
81
|
+
const count = context.params.shapeCount as number;
|
|
82
|
+
const noiseScale = context.params.noiseScale as number;
|
|
83
|
+
const rotSpeed = context.params.rotationSpeed as number;
|
|
84
|
+
|
|
85
|
+
p.push();
|
|
86
|
+
p.translate(p.width / 2, p.height / 2);
|
|
87
|
+
|
|
88
|
+
// Mouse influence
|
|
89
|
+
const mx = p.map(p.mouseX, 0, p.width, -1, 1);
|
|
90
|
+
const my = p.map(p.mouseY, 0, p.height, -1, 1);
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < count; i++) {
|
|
93
|
+
const angle = p.map(i, 0, count, 0, p.TWO_PI);
|
|
94
|
+
const noiseVal = p.noise(
|
|
95
|
+
p.cos(angle) * noiseScale * 100 + time,
|
|
96
|
+
p.sin(angle) * noiseScale * 100 + time,
|
|
97
|
+
time * 0.5
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const radius = 100 + noiseVal * 150 + mx * 50;
|
|
101
|
+
const x = p.cos(angle + time * rotSpeed) * radius;
|
|
102
|
+
const y = p.sin(angle + time * rotSpeed) * radius;
|
|
103
|
+
|
|
104
|
+
// Alternate colors
|
|
105
|
+
const color = i % 2 === 0
|
|
106
|
+
? context.params.primaryColor
|
|
107
|
+
: context.params.secondaryColor;
|
|
108
|
+
|
|
109
|
+
p.fill(color as string);
|
|
110
|
+
|
|
111
|
+
const size = 10 + noiseVal * 30 + my * 10;
|
|
112
|
+
p.ellipse(x, y, size, size);
|
|
113
|
+
|
|
114
|
+
// Inner ring
|
|
115
|
+
const innerRadius = radius * 0.6;
|
|
116
|
+
const ix = p.cos(angle - time * rotSpeed * 0.5) * innerRadius;
|
|
117
|
+
const iy = p.sin(angle - time * rotSpeed * 0.5) * innerRadius;
|
|
118
|
+
|
|
119
|
+
p.fill(color as string);
|
|
120
|
+
p.ellipse(ix, iy, size * 0.5, size * 0.5);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
p.pop();
|
|
124
|
+
|
|
125
|
+
time += 0.01;
|
|
126
|
+
};
|
|
127
|
+
}, context.mount);
|
|
128
|
+
|
|
129
|
+
// Register the p5 instance for external access
|
|
130
|
+
context.registerObject('p5Instance', sketch!, { type: 'p5' });
|
|
131
|
+
|
|
132
|
+
return () => {
|
|
133
|
+
sketch?.remove();
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>HyperTool SDK - React Example</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body {
|
|
10
|
+
font-family: system-ui, sans-serif;
|
|
11
|
+
background: #1a1a2e;
|
|
12
|
+
color: #fff;
|
|
13
|
+
min-height: 100vh;
|
|
14
|
+
}
|
|
15
|
+
#root { padding: 2rem; }
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<div id="root"></div>
|
|
20
|
+
<script type="module" src="./main.tsx"></script>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|