@alaagh/brainkit 2.0.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/CHANGELOG.md +50 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/cli.js +152 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +153 -0
- package/dist/skill-artifacts/threejs-performance-optimizer.skill +0 -0
- package/dist/skills/manifest.json +13 -0
- package/dist/skills/threejs-performance-optimizer/SKILL.md +100 -0
- package/dist/skills/threejs-performance-optimizer/references/assets-and-loaders.md +100 -0
- package/dist/skills/threejs-performance-optimizer/references/draw-calls-materials-lighting.md +78 -0
- package/dist/skills/threejs-performance-optimizer/references/memory-and-disposal.md +95 -0
- package/dist/skills/threejs-performance-optimizer/references/outdated-guidance.md +25 -0
- package/dist/skills/threejs-performance-optimizer/references/performance-workflow.md +99 -0
- package/dist/skills/threejs-performance-optimizer/references/r3f-rules.md +84 -0
- package/dist/skills/threejs-performance-optimizer/references/renderer-and-loop-patterns.md +113 -0
- package/dist/tools/manifest.json +4 -0
- package/package.json +57 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
## [2.0.0] - 2026-02-14
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Restructured repository into multi-skill layout with:
|
|
10
|
+
- `skills/*` for source skill folders
|
|
11
|
+
- `skill-artifacts/*.skill` for packaged artifacts
|
|
12
|
+
- Added npm package support under `@alaagh/brainkit`.
|
|
13
|
+
- Added CLI tool `brainkit` with commands for list/manifest/path/install.
|
|
14
|
+
- Added JavaScript API for listing and installing skills.
|
|
15
|
+
- Added comprehensive automated test suite covering API, CLI, build output, and scripts.
|
|
16
|
+
- Added release publish workflow for npm on GitHub release publish.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Renamed project/repo identity to `brainkit`.
|
|
21
|
+
- Upgraded CI to run tests on every push and pull request across Node 18/20/22.
|
|
22
|
+
|
|
23
|
+
### Removed
|
|
24
|
+
|
|
25
|
+
- Removed legacy single-skill root structure.
|
|
26
|
+
- Removed tracked `dist` artifact from repository; build output is generated.
|
|
27
|
+
|
|
28
|
+
## [1.1.0] - 2026-02-14
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Expanded README with badges, quick start, demo links, and community sections.
|
|
33
|
+
- Added contributor guidance in `CONTRIBUTING.md`.
|
|
34
|
+
- Added MIT `LICENSE`.
|
|
35
|
+
- Added docs set:
|
|
36
|
+
- `docs/DEMO.md`
|
|
37
|
+
- `docs/USE_CASES.md`
|
|
38
|
+
- `docs/API_REFERENCE.md`
|
|
39
|
+
- `docs/LAUNCH_PLAYBOOK.md`
|
|
40
|
+
- `docs/SHOWCASE.md`
|
|
41
|
+
- Added GitHub workflow for skill structure validation.
|
|
42
|
+
- Added issue templates and pull request template.
|
|
43
|
+
|
|
44
|
+
## [1.0.0] - 2026-02-14
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- Initial release of `threejs-performance-optimizer` skill.
|
|
49
|
+
- Reference modules for workflow, render loop, assets/loaders, draw calls, memory, and R3F.
|
|
50
|
+
- Packaged distributable skill artifact.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alaa Alghazouli
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# brainkit
|
|
2
|
+
|
|
3
|
+
[](https://github.com/alaa-alghazouli/brainkit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/alaa-alghazouli/brainkit/releases)
|
|
5
|
+
[](https://www.npmjs.com/package/@alaagh/brainkit)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
`brainkit` is an installable npm package for AI-ready assets:
|
|
9
|
+
|
|
10
|
+
- skills
|
|
11
|
+
- skill artifacts (`.skill`)
|
|
12
|
+
- tool registry space (future)
|
|
13
|
+
|
|
14
|
+
It currently includes one production skill: `threejs-performance-optimizer`.
|
|
15
|
+
|
|
16
|
+
## Install with npm
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @alaagh/brainkit
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Install the skill
|
|
23
|
+
|
|
24
|
+
1. List available skills:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx brainkit list
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Install the packaged skill artifact:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx brainkit install threejs-performance-optimizer --dest ~/.config/opencode/skills
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This creates:
|
|
37
|
+
|
|
38
|
+
`~/.config/opencode/skills/threejs-performance-optimizer.skill`
|
|
39
|
+
|
|
40
|
+
3. Optional: install source folder instead of `.skill` artifact:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx brainkit install threejs-performance-optimizer --mode source --dest ~/.config/opencode/skills
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## CLI quick reference
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
brainkit list
|
|
50
|
+
brainkit manifest
|
|
51
|
+
brainkit catalog
|
|
52
|
+
brainkit where threejs-performance-optimizer
|
|
53
|
+
brainkit install <skill|all> --dest <path> [--mode artifact|source]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## JavaScript API
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import { listSkills, installSkill } from "@alaagh/brainkit";
|
|
60
|
+
|
|
61
|
+
const skills = listSkills();
|
|
62
|
+
await installSkill("threejs-performance-optimizer", "./local-skills");
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## What is included today
|
|
66
|
+
|
|
67
|
+
- `threejs-performance-optimizer`: profile-first optimization playbook for Three.js and React Three Fiber performance.
|
|
68
|
+
|
|
69
|
+
## Repository layout
|
|
70
|
+
|
|
71
|
+
- `skills/` source skills
|
|
72
|
+
- `skill-artifacts/` packaged `.skill` files
|
|
73
|
+
- `tools/` AI tool metadata and future tool bundles
|
|
74
|
+
- `src/` package API and CLI implementation
|
|
75
|
+
- `scripts/` build and validation scripts
|
|
76
|
+
- `tests/` API, CLI, build, and script tests
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install
|
|
82
|
+
npm test
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Contributing
|
|
86
|
+
|
|
87
|
+
See `CONTRIBUTING.md`.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getManifest,
|
|
5
|
+
getSkillArtifactPath,
|
|
6
|
+
getSkillSourcePath,
|
|
7
|
+
getToolsManifest,
|
|
8
|
+
installAll,
|
|
9
|
+
installSkill,
|
|
10
|
+
listSkills,
|
|
11
|
+
} from "./index.js";
|
|
12
|
+
|
|
13
|
+
function parseOptions(argv) {
|
|
14
|
+
const options = {
|
|
15
|
+
mode: "artifact",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
19
|
+
const token = argv[index];
|
|
20
|
+
|
|
21
|
+
if (token === "--dest") {
|
|
22
|
+
options.dest = argv[index + 1];
|
|
23
|
+
index += 1;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (token === "--mode") {
|
|
28
|
+
options.mode = argv[index + 1];
|
|
29
|
+
index += 1;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return options;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function printHelp() {
|
|
38
|
+
console.log(`brainkit CLI
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
brainkit list
|
|
42
|
+
brainkit catalog
|
|
43
|
+
brainkit manifest
|
|
44
|
+
brainkit where <skill> [--mode artifact|source]
|
|
45
|
+
brainkit install <skill|all> --dest <path> [--mode artifact|source]
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
brainkit list
|
|
49
|
+
brainkit catalog
|
|
50
|
+
brainkit install threejs-performance-optimizer --dest ~/.config/opencode/skills
|
|
51
|
+
brainkit install all --mode source --dest ./local-skills
|
|
52
|
+
`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function run() {
|
|
56
|
+
const argv = process.argv.slice(2);
|
|
57
|
+
const command = argv[0];
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
!command ||
|
|
61
|
+
command === "help" ||
|
|
62
|
+
command === "--help" ||
|
|
63
|
+
command === "-h"
|
|
64
|
+
) {
|
|
65
|
+
printHelp();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (command === "list") {
|
|
70
|
+
const skills = listSkills();
|
|
71
|
+
for (const skill of skills) {
|
|
72
|
+
console.log(skill);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (command === "manifest") {
|
|
78
|
+
console.log(JSON.stringify(getManifest(), null, 2));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (command === "catalog") {
|
|
83
|
+
const manifest = getManifest();
|
|
84
|
+
const tools = getToolsManifest();
|
|
85
|
+
|
|
86
|
+
console.log(
|
|
87
|
+
JSON.stringify(
|
|
88
|
+
{
|
|
89
|
+
version: 1,
|
|
90
|
+
skills: manifest.skills,
|
|
91
|
+
tools: tools.tools,
|
|
92
|
+
},
|
|
93
|
+
null,
|
|
94
|
+
2,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (command === "where") {
|
|
101
|
+
const skillName = argv[1];
|
|
102
|
+
if (!skillName) {
|
|
103
|
+
throw new Error('Skill name is required for "where" command.');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const options = parseOptions(argv.slice(2));
|
|
107
|
+
if (options.mode === "source") {
|
|
108
|
+
console.log(getSkillSourcePath(skillName));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(getSkillArtifactPath(skillName));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (command === "install") {
|
|
117
|
+
const skillName = argv[1];
|
|
118
|
+
if (!skillName) {
|
|
119
|
+
throw new Error('Skill name is required for "install" command.');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const options = parseOptions(argv.slice(2));
|
|
123
|
+
if (!options.dest) {
|
|
124
|
+
throw new Error("Destination path is required. Use --dest <path>.");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!["artifact", "source"].includes(options.mode)) {
|
|
128
|
+
throw new Error("Invalid mode. Use --mode artifact or --mode source.");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (skillName === "all") {
|
|
132
|
+
const results = await installAll(options.dest, { mode: options.mode });
|
|
133
|
+
for (const result of results) {
|
|
134
|
+
console.log(`${result.skill} -> ${result.outputPath}`);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const result = await installSkill(skillName, options.dest, {
|
|
140
|
+
mode: options.mode,
|
|
141
|
+
});
|
|
142
|
+
console.log(`${result.skill} -> ${result.outputPath}`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error(`Unknown command: ${command}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
run().catch((error) => {
|
|
150
|
+
console.error(error.message);
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type InstallMode = "artifact" | "source";
|
|
2
|
+
|
|
3
|
+
export interface InstallOptions {
|
|
4
|
+
mode?: InstallMode;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface InstallResult {
|
|
8
|
+
skill: string;
|
|
9
|
+
mode: InstallMode;
|
|
10
|
+
outputPath: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SkillManifestEntry {
|
|
14
|
+
slug: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
version?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SkillManifest {
|
|
21
|
+
version: number;
|
|
22
|
+
skills: SkillManifestEntry[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ToolManifestEntry {
|
|
26
|
+
slug: string;
|
|
27
|
+
name?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
version?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ToolManifest {
|
|
33
|
+
version: number;
|
|
34
|
+
tools: ToolManifestEntry[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function listSkills(): string[];
|
|
38
|
+
export function getManifest(): SkillManifest;
|
|
39
|
+
export function getToolsManifest(): ToolManifest;
|
|
40
|
+
export function getSkillSourcePath(skillName: string): string;
|
|
41
|
+
export function getSkillArtifactPath(skillName: string): string;
|
|
42
|
+
export function installSkill(
|
|
43
|
+
skillName: string,
|
|
44
|
+
destination: string,
|
|
45
|
+
options?: InstallOptions,
|
|
46
|
+
): Promise<InstallResult>;
|
|
47
|
+
export function installAll(
|
|
48
|
+
destination: string,
|
|
49
|
+
options?: InstallOptions,
|
|
50
|
+
): Promise<InstallResult[]>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cpSync,
|
|
3
|
+
copyFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const moduleDirectory = fileURLToPath(new URL(".", import.meta.url));
|
|
13
|
+
const localSkillsDir = path.join(moduleDirectory, "skills");
|
|
14
|
+
const localArtifactsDir = path.join(moduleDirectory, "skill-artifacts");
|
|
15
|
+
const dataRoot =
|
|
16
|
+
existsSync(localSkillsDir) && existsSync(localArtifactsDir)
|
|
17
|
+
? moduleDirectory
|
|
18
|
+
: path.resolve(moduleDirectory, "..");
|
|
19
|
+
|
|
20
|
+
const skillsDir = path.join(dataRoot, "skills");
|
|
21
|
+
const artifactsDir = path.join(dataRoot, "skill-artifacts");
|
|
22
|
+
const manifestPath = path.join(skillsDir, "manifest.json");
|
|
23
|
+
const toolsManifestPath = path.join(dataRoot, "tools", "manifest.json");
|
|
24
|
+
const validInstallModes = new Set(["artifact", "source"]);
|
|
25
|
+
|
|
26
|
+
function ensureDirectoryExists(directoryPath, label) {
|
|
27
|
+
if (!existsSync(directoryPath)) {
|
|
28
|
+
throw new Error(`${label} not found: ${directoryPath}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolveDestination(destination) {
|
|
33
|
+
if (!destination) {
|
|
34
|
+
throw new Error("Destination path is required.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const absolutePath = path.resolve(destination);
|
|
38
|
+
mkdirSync(absolutePath, { recursive: true });
|
|
39
|
+
return absolutePath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function assertSkillExists(skillName) {
|
|
43
|
+
const availableSkills = listSkills();
|
|
44
|
+
if (!availableSkills.includes(skillName)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Unknown skill: ${skillName}. Available skills: ${availableSkills.join(", ")}`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveInstallMode(mode) {
|
|
52
|
+
const installMode = mode ?? "artifact";
|
|
53
|
+
|
|
54
|
+
if (!validInstallModes.has(installMode)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Invalid mode: ${installMode}. Use \"artifact\" or \"source\".`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return installMode;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function listSkills() {
|
|
64
|
+
ensureDirectoryExists(skillsDir, "Skills directory");
|
|
65
|
+
|
|
66
|
+
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
67
|
+
return entries
|
|
68
|
+
.filter((entry) => entry.isDirectory())
|
|
69
|
+
.map((entry) => entry.name)
|
|
70
|
+
.sort();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getManifest() {
|
|
74
|
+
if (!existsSync(manifestPath)) {
|
|
75
|
+
return {
|
|
76
|
+
version: 1,
|
|
77
|
+
skills: listSkills().map((skillName) => ({
|
|
78
|
+
slug: skillName,
|
|
79
|
+
})),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function getToolsManifest() {
|
|
87
|
+
if (!existsSync(toolsManifestPath)) {
|
|
88
|
+
return {
|
|
89
|
+
version: 1,
|
|
90
|
+
tools: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return JSON.parse(readFileSync(toolsManifestPath, "utf8"));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getSkillSourcePath(skillName) {
|
|
98
|
+
assertSkillExists(skillName);
|
|
99
|
+
return path.join(skillsDir, skillName);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getSkillArtifactPath(skillName) {
|
|
103
|
+
assertSkillExists(skillName);
|
|
104
|
+
ensureDirectoryExists(artifactsDir, "Skill artifacts directory");
|
|
105
|
+
|
|
106
|
+
const artifactPath = path.join(artifactsDir, `${skillName}.skill`);
|
|
107
|
+
if (!existsSync(artifactPath)) {
|
|
108
|
+
throw new Error(`Missing artifact for ${skillName}: ${artifactPath}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return artifactPath;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function installSkill(skillName, destination, options = {}) {
|
|
115
|
+
const mode = resolveInstallMode(options.mode);
|
|
116
|
+
const outputDirectory = resolveDestination(destination);
|
|
117
|
+
|
|
118
|
+
if (mode === "source") {
|
|
119
|
+
const sourcePath = getSkillSourcePath(skillName);
|
|
120
|
+
const targetPath = path.join(outputDirectory, skillName);
|
|
121
|
+
|
|
122
|
+
cpSync(sourcePath, targetPath, { recursive: true, force: true });
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
skill: skillName,
|
|
126
|
+
mode,
|
|
127
|
+
outputPath: targetPath,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const artifactPath = getSkillArtifactPath(skillName);
|
|
132
|
+
const targetPath = path.join(outputDirectory, `${skillName}.skill`);
|
|
133
|
+
|
|
134
|
+
copyFileSync(artifactPath, targetPath);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
skill: skillName,
|
|
138
|
+
mode,
|
|
139
|
+
outputPath: targetPath,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function installAll(destination, options = {}) {
|
|
144
|
+
const skills = listSkills();
|
|
145
|
+
const installations = [];
|
|
146
|
+
|
|
147
|
+
for (const skillName of skills) {
|
|
148
|
+
const result = await installSkill(skillName, destination, options);
|
|
149
|
+
installations.push(result);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return installations;
|
|
153
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"skills": [
|
|
4
|
+
{
|
|
5
|
+
"slug": "threejs-performance-optimizer",
|
|
6
|
+
"name": "Threejs Performance Optimizer",
|
|
7
|
+
"description": "Profile-first optimization workflow for Three.js and React Three Fiber performance.",
|
|
8
|
+
"version": "1.1.0",
|
|
9
|
+
"artifact": "threejs-performance-optimizer.skill",
|
|
10
|
+
"source": "skills/threejs-performance-optimizer"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: threejs-performance-optimizer
|
|
3
|
+
description: Optimize and refactor Three.js and React Three Fiber code for runtime performance, memory efficiency, loading speed, and rendering quality using a profile-first workflow grounded in current Three.js and R3F docs. Use when requests mention low FPS, frame drops, stutter/jank, high draw calls, heavy post-processing, shadow cost, large GLB/texture payloads, mobile 3D slowness, memory leaks, dispose/cleanup issues, WebGPU or WebGL fallback concerns, frameloop demand/invalidate patterns, instancing, batching, LOD, or general Three.js performance tuning.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Threejs Performance Optimizer
|
|
7
|
+
|
|
8
|
+
Optimize performance by measurement, not folklore. Prioritize high-impact changes, preserve visual intent, and avoid stale APIs.
|
|
9
|
+
|
|
10
|
+
## Core Behavior
|
|
11
|
+
|
|
12
|
+
Follow this sequence every time:
|
|
13
|
+
|
|
14
|
+
1. Measure baseline first.
|
|
15
|
+
2. Classify the bottleneck (CPU, GPU, memory, or loading).
|
|
16
|
+
3. Apply one focused optimization pass.
|
|
17
|
+
4. Re-measure and keep only verified improvements.
|
|
18
|
+
|
|
19
|
+
Never start by rewriting everything. Prefer the smallest change that hits the biggest bottleneck.
|
|
20
|
+
|
|
21
|
+
## Reference Loading Order
|
|
22
|
+
|
|
23
|
+
Read references in this order and stop when enough context is collected:
|
|
24
|
+
|
|
25
|
+
1. `references/performance-workflow.md` for triage and reporting structure.
|
|
26
|
+
2. `references/renderer-and-loop-patterns.md` for render loop and renderer choices.
|
|
27
|
+
3. `references/assets-and-loaders.md` for asset and decoder/transcoder pipeline.
|
|
28
|
+
4. `references/draw-calls-materials-lighting.md` for scene/render cost tuning.
|
|
29
|
+
5. `references/memory-and-disposal.md` for leaks, cleanup, and update flags.
|
|
30
|
+
6. `references/r3f-rules.md` when code uses React Three Fiber.
|
|
31
|
+
7. `references/outdated-guidance.md` before finalizing recommendations.
|
|
32
|
+
|
|
33
|
+
## Mandatory Workflow
|
|
34
|
+
|
|
35
|
+
### 1) Measure
|
|
36
|
+
|
|
37
|
+
Collect at least:
|
|
38
|
+
|
|
39
|
+
- FPS or frame time trend.
|
|
40
|
+
- Draw calls and triangles (`renderer.info`).
|
|
41
|
+
- Memory trend for textures/geometries/programs.
|
|
42
|
+
- Main-thread and GPU hotspots from profiler.
|
|
43
|
+
|
|
44
|
+
If performance metrics are missing, request them or add lightweight instrumentation.
|
|
45
|
+
|
|
46
|
+
### 2) Classify
|
|
47
|
+
|
|
48
|
+
Classify the primary bottleneck before prescribing fixes:
|
|
49
|
+
|
|
50
|
+
- CPU-bound: heavy JS/React work, allocations, simulation, or scene graph churn.
|
|
51
|
+
- GPU-bound: high draw call count, expensive shaders/lights/shadows/postprocessing.
|
|
52
|
+
- Memory-bound: growing texture/geometry/render-target counts, context pressure.
|
|
53
|
+
- Load-time bound: large payloads, slow decode/transcode, blocking startup work.
|
|
54
|
+
|
|
55
|
+
### 3) Fix in ROI Order
|
|
56
|
+
|
|
57
|
+
Use this order unless data proves otherwise:
|
|
58
|
+
|
|
59
|
+
1. Asset payload and decode pipeline.
|
|
60
|
+
2. Draw call reduction (instancing, batching, static merges).
|
|
61
|
+
3. Render loop policy (on-demand and adaptive DPR where suitable).
|
|
62
|
+
4. Lighting/shadow/postprocessing cost.
|
|
63
|
+
5. Memory lifecycle and disposal correctness.
|
|
64
|
+
|
|
65
|
+
### 4) Verify and Report
|
|
66
|
+
|
|
67
|
+
Report:
|
|
68
|
+
|
|
69
|
+
- What changed.
|
|
70
|
+
- Expected impact.
|
|
71
|
+
- Measured before/after.
|
|
72
|
+
- Tradeoffs and rollback criteria.
|
|
73
|
+
|
|
74
|
+
## Non-Negotiable Rules
|
|
75
|
+
|
|
76
|
+
- Prefer official, current APIs over old blog snippets.
|
|
77
|
+
- Treat hard numeric targets as heuristics, not universal truths.
|
|
78
|
+
- Keep static scenes from rendering continuously when not needed.
|
|
79
|
+
- Avoid per-frame object allocation in hot paths.
|
|
80
|
+
- Explicitly dispose resources no longer needed.
|
|
81
|
+
- Keep anti-aliasing strategy profile-driven, not dogmatic.
|
|
82
|
+
|
|
83
|
+
## Hard Anti-Rules
|
|
84
|
+
|
|
85
|
+
- Do not use deprecated renderer APIs in recommendations.
|
|
86
|
+
- Do not claim guaranteed multipliers (2x, 10x) without measurement.
|
|
87
|
+
- Do not assume removing from scene frees GPU memory.
|
|
88
|
+
- Do not use React state updates for per-frame animation.
|
|
89
|
+
- Do not force one compression or AA approach for all projects.
|
|
90
|
+
|
|
91
|
+
## Output Contract
|
|
92
|
+
|
|
93
|
+
When delivering optimization advice or edits, always include:
|
|
94
|
+
|
|
95
|
+
1. Bottleneck diagnosis.
|
|
96
|
+
2. Ordered action plan.
|
|
97
|
+
3. Risk notes (visual quality, compatibility, complexity).
|
|
98
|
+
4. Verification steps and expected metrics movement.
|
|
99
|
+
|
|
100
|
+
Keep recommendations practical, testable, and version-safe.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Assets and Loaders
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Asset Strategy
|
|
6
|
+
- CLI Pipeline
|
|
7
|
+
- Loader Wiring
|
|
8
|
+
- Compression Choices
|
|
9
|
+
- Loading UX
|
|
10
|
+
|
|
11
|
+
## Asset Strategy
|
|
12
|
+
|
|
13
|
+
Prefer glTF/GLB for runtime delivery.
|
|
14
|
+
|
|
15
|
+
Apply optimizations in this order:
|
|
16
|
+
|
|
17
|
+
1. Remove unnecessary geometry and textures at source.
|
|
18
|
+
2. Compress geometry (Draco or Meshopt).
|
|
19
|
+
3. Compress textures (KTX2 Basis ETC1S or UASTC).
|
|
20
|
+
4. Verify visual quality and decode cost on target devices.
|
|
21
|
+
|
|
22
|
+
## CLI Pipeline
|
|
23
|
+
|
|
24
|
+
Use inspection before optimization:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gltf-transform inspect input.glb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use a baseline optimization pass:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
gltf-transform optimize input.glb output.glb --compress draco --texture-compress webp
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use KTX2 workflows when GPU texture compression is needed:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gltf-transform etc1s input.glb output-etc1s.glb
|
|
40
|
+
gltf-transform uastc input.glb output-uastc.glb
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For R3F projects, consider:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx gltfjsx model.glb -S -T -t
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Loader Wiring
|
|
50
|
+
|
|
51
|
+
Configure shared loaders once and reuse instances:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
55
|
+
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
|
|
56
|
+
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
|
|
57
|
+
|
|
58
|
+
const draco = new DRACOLoader().setDecoderPath("/draco/");
|
|
59
|
+
const ktx2 = new KTX2Loader()
|
|
60
|
+
.setTranscoderPath("/basis/")
|
|
61
|
+
.detectSupport(renderer);
|
|
62
|
+
|
|
63
|
+
const gltfLoader = new GLTFLoader().setDRACOLoader(draco).setKTX2Loader(ktx2);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Important loader rules:
|
|
67
|
+
|
|
68
|
+
- Call `detectSupport(renderer)` before KTX2 loads.
|
|
69
|
+
- Reuse decoder/transcoder instances to avoid repeated startup overhead.
|
|
70
|
+
- Dispose loader internals when the loader is no longer needed.
|
|
71
|
+
|
|
72
|
+
## Compression Choices
|
|
73
|
+
|
|
74
|
+
Use content-aware defaults:
|
|
75
|
+
|
|
76
|
+
- ETC1S: smaller files, higher compression artifacts.
|
|
77
|
+
- UASTC: higher quality, larger files.
|
|
78
|
+
|
|
79
|
+
Evaluate Draco vs Meshopt per project:
|
|
80
|
+
|
|
81
|
+
- Draco often yields strong geometry compression.
|
|
82
|
+
- Meshopt may decode faster in some scenarios.
|
|
83
|
+
|
|
84
|
+
Always compare:
|
|
85
|
+
|
|
86
|
+
- Network transfer size.
|
|
87
|
+
- Decode/transcode time.
|
|
88
|
+
- Runtime memory.
|
|
89
|
+
- Visual quality.
|
|
90
|
+
|
|
91
|
+
## Loading UX
|
|
92
|
+
|
|
93
|
+
Improve startup behavior:
|
|
94
|
+
|
|
95
|
+
- Preload only above-the-fold critical assets.
|
|
96
|
+
- Lazy-load offscreen 3D sections.
|
|
97
|
+
- Use placeholder or low-detail proxies while heavy assets load.
|
|
98
|
+
- Stream or chunk large worlds instead of one monolithic load.
|
|
99
|
+
|
|
100
|
+
Treat loading optimization as performance work, not only UX polish.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Draw Calls, Materials, Lighting
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Draw Call Reduction
|
|
6
|
+
- Geometry Strategy
|
|
7
|
+
- Material Strategy
|
|
8
|
+
- Lighting and Shadows
|
|
9
|
+
- Postprocessing
|
|
10
|
+
|
|
11
|
+
## Draw Call Reduction
|
|
12
|
+
|
|
13
|
+
Treat draw-call reduction as a primary lever.
|
|
14
|
+
|
|
15
|
+
Use this escalation path:
|
|
16
|
+
|
|
17
|
+
1. Share materials and geometry where possible.
|
|
18
|
+
2. Use `InstancedMesh` for repeated objects.
|
|
19
|
+
3. Use batch or merged static geometry for non-interactive sets.
|
|
20
|
+
4. Use LOD and distance-based simplification.
|
|
21
|
+
|
|
22
|
+
Use draw-call budgets as heuristics, not hard guarantees. Validate on target devices.
|
|
23
|
+
|
|
24
|
+
## Geometry Strategy
|
|
25
|
+
|
|
26
|
+
For repeated meshes, prefer instancing:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const mesh = new THREE.InstancedMesh(geometry, material, count);
|
|
30
|
+
for (let i = 0; i < count; i += 1) {
|
|
31
|
+
mesh.setMatrixAt(i, matrix);
|
|
32
|
+
}
|
|
33
|
+
mesh.instanceMatrix.needsUpdate = true;
|
|
34
|
+
mesh.computeBoundingSphere();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For static scene chunks, consider merged geometry:
|
|
38
|
+
|
|
39
|
+
- Merge by material compatibility.
|
|
40
|
+
- Keep interactable parts separate.
|
|
41
|
+
- Avoid over-merging when per-object behavior is required.
|
|
42
|
+
|
|
43
|
+
## Material Strategy
|
|
44
|
+
|
|
45
|
+
Control shader variant explosion:
|
|
46
|
+
|
|
47
|
+
- Reuse shared material instances whenever possible.
|
|
48
|
+
- Avoid unnecessary feature toggles at runtime that trigger recompilation.
|
|
49
|
+
- Update uniforms/data values instead of replacing whole materials.
|
|
50
|
+
|
|
51
|
+
Prefer simpler materials where quality permits.
|
|
52
|
+
|
|
53
|
+
## Lighting and Shadows
|
|
54
|
+
|
|
55
|
+
Use dynamic lights selectively:
|
|
56
|
+
|
|
57
|
+
- Keep active shadow-casting lights minimal.
|
|
58
|
+
- Tighten shadow camera frustums.
|
|
59
|
+
- Use smallest acceptable shadow map sizes.
|
|
60
|
+
- Disable shadow auto-updates for static scenes and trigger only when needed.
|
|
61
|
+
|
|
62
|
+
Point-light shadow cost reminder:
|
|
63
|
+
|
|
64
|
+
- One point light shadow map requires six faces.
|
|
65
|
+
- Cost scales quickly with object count.
|
|
66
|
+
|
|
67
|
+
Prefer environment lighting and baked/static lighting when practical.
|
|
68
|
+
|
|
69
|
+
## Postprocessing
|
|
70
|
+
|
|
71
|
+
Treat each pass as measurable cost.
|
|
72
|
+
|
|
73
|
+
- Start with the minimal pass chain.
|
|
74
|
+
- Merge compatible effects when possible.
|
|
75
|
+
- Re-test antialiasing strategy per project.
|
|
76
|
+
- Reduce internal effect resolution when quality permits.
|
|
77
|
+
|
|
78
|
+
Avoid rigid recommendations like "always disable native AA" or "always use FXAA". Measure and choose.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Memory and Disposal
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Disposal Rules
|
|
6
|
+
- Cleanup Patterns
|
|
7
|
+
- Update Flags
|
|
8
|
+
- Bounding Volumes
|
|
9
|
+
- Leak Detection
|
|
10
|
+
|
|
11
|
+
## Disposal Rules
|
|
12
|
+
|
|
13
|
+
Removing an object from a scene does not free GPU resources.
|
|
14
|
+
|
|
15
|
+
Explicitly dispose resources no longer needed:
|
|
16
|
+
|
|
17
|
+
- `BufferGeometry.dispose()`
|
|
18
|
+
- `Material.dispose()`
|
|
19
|
+
- `Texture.dispose()`
|
|
20
|
+
- `WebGLRenderTarget.dispose()`
|
|
21
|
+
- `Skeleton.dispose()` for unused shared skeletons
|
|
22
|
+
|
|
23
|
+
If texture data uses `ImageBitmap`, close CPU-side data explicitly:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
texture.source.data.close?.();
|
|
27
|
+
texture.dispose();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Cleanup Patterns
|
|
31
|
+
|
|
32
|
+
Use a traversal cleanup utility for scene teardown:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
function disposeScene(root) {
|
|
36
|
+
root.traverse((obj) => {
|
|
37
|
+
if (obj.geometry) {
|
|
38
|
+
obj.geometry.dispose();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!obj.material) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const materials = Array.isArray(obj.material)
|
|
46
|
+
? obj.material
|
|
47
|
+
: [obj.material];
|
|
48
|
+
for (const material of materials) {
|
|
49
|
+
for (const key of Object.keys(material)) {
|
|
50
|
+
const value = material[key];
|
|
51
|
+
if (value && value.isTexture) {
|
|
52
|
+
value.dispose();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
material.dispose();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Dispose loader internals or worker resources when loader lifecycle ends.
|
|
62
|
+
|
|
63
|
+
## Update Flags
|
|
64
|
+
|
|
65
|
+
Use update flags only when required:
|
|
66
|
+
|
|
67
|
+
- Buffer attribute changes: `attribute.needsUpdate = true`
|
|
68
|
+
- Texture content changes: `texture.needsUpdate = true`
|
|
69
|
+
- Material feature or define changes: `material.needsUpdate = true`
|
|
70
|
+
|
|
71
|
+
For static objects, disable automatic matrix updates:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
object.matrixAutoUpdate = false;
|
|
75
|
+
object.updateMatrix();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Bounding Volumes
|
|
79
|
+
|
|
80
|
+
Recompute bounds after structural data updates:
|
|
81
|
+
|
|
82
|
+
- Geometry vertex changes: `computeBoundingBox()` and `computeBoundingSphere()`
|
|
83
|
+
- Instanced transforms update: `instancedMesh.computeBoundingBox()` and `computeBoundingSphere()`
|
|
84
|
+
|
|
85
|
+
Accurate bounds protect culling and interaction correctness.
|
|
86
|
+
|
|
87
|
+
## Leak Detection
|
|
88
|
+
|
|
89
|
+
Track `renderer.info` over repeated scene mount/unmount cycles:
|
|
90
|
+
|
|
91
|
+
- `renderer.info.memory.textures`
|
|
92
|
+
- `renderer.info.memory.geometries`
|
|
93
|
+
- `renderer.info.programs`
|
|
94
|
+
|
|
95
|
+
Stable plateaus are normal. Monotonic growth under repeated teardown/rebuild indicates leaks.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Outdated Guidance Guardrails
|
|
2
|
+
|
|
3
|
+
Use this table to avoid stale recommendations.
|
|
4
|
+
|
|
5
|
+
| Avoid this guidance | Why | Use this instead |
|
|
6
|
+
| ----------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------- |
|
|
7
|
+
| "Use `renderAsync()` in the main loop" | Deprecated in current renderer docs | Use `setAnimationLoop()` with `render()` and explicit compute workflow |
|
|
8
|
+
| "Use `gammaFactor` / `outputEncoding` as default advice" | Legacy color pipeline terminology | Use current color-space workflow and current renderer properties |
|
|
9
|
+
| "`Geometry` / `BoxBufferGeometry` naming as modern default" | Legacy naming and old-era examples | Use current geometry APIs and confirm version-specific docs |
|
|
10
|
+
| "All textures must be power-of-two" | Too absolute for modern contexts | Prefer POT for compatibility/perf-sensitive paths; profile and validate |
|
|
11
|
+
| "Always disable native MSAA and use FXAA/SMAA" | Highly scene/device dependent | Compare native AA vs post AA using measurements |
|
|
12
|
+
| "Always keep under 100 draw calls" | Useful heuristic, not universal rule | Use target-device budgets and verify frame stability |
|
|
13
|
+
| "Removing mesh from scene frees memory" | Incorrect resource lifecycle model | Explicitly call `dispose()` on geometry/material/texture/targets |
|
|
14
|
+
| "Use React state for per-frame animation" | Causes re-render overhead and jank | Mutate refs in `useFrame`, use state for coarse events |
|
|
15
|
+
| "Always migrate to WebGPU now" | Context-dependent project constraints | Evaluate project requirements, browser/device support, and measured gains |
|
|
16
|
+
|
|
17
|
+
## Source Confidence Priority
|
|
18
|
+
|
|
19
|
+
Prefer recommendations in this order:
|
|
20
|
+
|
|
21
|
+
1. Current official docs and API reference.
|
|
22
|
+
2. Official manuals and examples.
|
|
23
|
+
3. Community articles and blog heuristics.
|
|
24
|
+
|
|
25
|
+
When community advice conflicts with official docs, prioritize docs and annotate the conflict.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Performance Workflow
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Baseline Checklist
|
|
6
|
+
- Bottleneck Classification
|
|
7
|
+
- Priority Matrix
|
|
8
|
+
- Verification Template
|
|
9
|
+
|
|
10
|
+
## Baseline Checklist
|
|
11
|
+
|
|
12
|
+
Capture these values before touching code:
|
|
13
|
+
|
|
14
|
+
- Runtime profile: FPS and frame-time variance (average and worst spikes).
|
|
15
|
+
- Renderer stats: `renderer.info.render.calls`, triangles, and points.
|
|
16
|
+
- Memory trend: `renderer.info.memory.textures`, geometries, and programs.
|
|
17
|
+
- Startup profile: bundle size, model size, texture payload, decode/transcode duration.
|
|
18
|
+
- Device context: desktop/mobile, GPU class, browser, resolution, DPR.
|
|
19
|
+
|
|
20
|
+
Use at least one user interaction scenario and one idle scenario.
|
|
21
|
+
|
|
22
|
+
## Bottleneck Classification
|
|
23
|
+
|
|
24
|
+
Use this decision path:
|
|
25
|
+
|
|
26
|
+
1. Check profiler for long JS tasks or React re-render churn.
|
|
27
|
+
2. Check draw calls, shader complexity, light/shadow/post stack cost.
|
|
28
|
+
3. Check memory growth over time in repeated navigation or scene swaps.
|
|
29
|
+
4. Check network/decode/transcode delays before first interactive frame.
|
|
30
|
+
|
|
31
|
+
Interpretation guide:
|
|
32
|
+
|
|
33
|
+
- CPU-bound indicators:
|
|
34
|
+
- Frequent long tasks.
|
|
35
|
+
- Per-frame allocations.
|
|
36
|
+
- High-frequency state updates.
|
|
37
|
+
- GPU-bound indicators:
|
|
38
|
+
- High draw calls.
|
|
39
|
+
- Heavy transparency, postprocessing, shadow passes.
|
|
40
|
+
- Lower FPS with high DPR and dense scene content.
|
|
41
|
+
- Memory-bound indicators:
|
|
42
|
+
- Steadily growing texture/geometry counts.
|
|
43
|
+
- Context loss or large spikes during scene transitions.
|
|
44
|
+
- Load-time bound indicators:
|
|
45
|
+
- Long time-to-first-render.
|
|
46
|
+
- Large GLB and texture payloads.
|
|
47
|
+
- Decoder/transcoder setup errors or delays.
|
|
48
|
+
|
|
49
|
+
## Priority Matrix
|
|
50
|
+
|
|
51
|
+
Apply fixes in this order unless measurements suggest otherwise.
|
|
52
|
+
|
|
53
|
+
1. Asset pipeline:
|
|
54
|
+
- Compress geometry and textures.
|
|
55
|
+
- Remove unused assets.
|
|
56
|
+
- Preload only critical resources.
|
|
57
|
+
2. Draw-call pressure:
|
|
58
|
+
- Instancing and batching.
|
|
59
|
+
- Merge static geometry.
|
|
60
|
+
- Share materials.
|
|
61
|
+
3. Render-loop policy:
|
|
62
|
+
- On-demand rendering where possible.
|
|
63
|
+
- Adaptive DPR and quality scaling.
|
|
64
|
+
4. Shading and lighting:
|
|
65
|
+
- Reduce dynamic lights/shadows.
|
|
66
|
+
- Tighten shadow frustums and map sizes.
|
|
67
|
+
- Trim postprocessing passes.
|
|
68
|
+
5. Memory lifecycle:
|
|
69
|
+
- Dispose obsolete resources.
|
|
70
|
+
- Reuse pooled objects.
|
|
71
|
+
|
|
72
|
+
Heuristic budgets (starting points only):
|
|
73
|
+
|
|
74
|
+
- Draw calls: aim for a few hundred or less on typical target hardware.
|
|
75
|
+
- Dynamic shadowed point lights: keep minimal, each adds substantial pass cost.
|
|
76
|
+
- DPR on mobile: cap aggressively when frame stability drops.
|
|
77
|
+
|
|
78
|
+
Do not treat these as hard rules. Validate against project goals.
|
|
79
|
+
|
|
80
|
+
## Verification Template
|
|
81
|
+
|
|
82
|
+
Use this response structure after each optimization pass:
|
|
83
|
+
|
|
84
|
+
- Baseline:
|
|
85
|
+
- FPS/frame time:
|
|
86
|
+
- Draw calls:
|
|
87
|
+
- Memory counters:
|
|
88
|
+
- Change applied:
|
|
89
|
+
- What changed:
|
|
90
|
+
- Why it targets the bottleneck:
|
|
91
|
+
- Result:
|
|
92
|
+
- New FPS/frame time:
|
|
93
|
+
- New draw calls:
|
|
94
|
+
- Memory delta:
|
|
95
|
+
- Tradeoffs:
|
|
96
|
+
- Visual impact:
|
|
97
|
+
- Compatibility risk:
|
|
98
|
+
- Complexity cost:
|
|
99
|
+
- Keep or rollback decision:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# React Three Fiber Rules
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Frame Loop Discipline
|
|
6
|
+
- Resource Reuse
|
|
7
|
+
- Rendering Policy
|
|
8
|
+
- Adaptive Quality
|
|
9
|
+
- Strict Anti-Patterns
|
|
10
|
+
|
|
11
|
+
## Frame Loop Discipline
|
|
12
|
+
|
|
13
|
+
Use mutation in `useFrame` for high-frequency updates.
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
const ref = useRef();
|
|
17
|
+
|
|
18
|
+
useFrame((_, delta) => {
|
|
19
|
+
ref.current.rotation.y += delta;
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use `delta` for frame-rate independence.
|
|
24
|
+
|
|
25
|
+
Avoid `setState` in `useFrame` or any high-frequency event loop.
|
|
26
|
+
|
|
27
|
+
## Resource Reuse
|
|
28
|
+
|
|
29
|
+
Reuse geometry/material instances and loaded assets:
|
|
30
|
+
|
|
31
|
+
- Use `useLoader` for cached resource loading.
|
|
32
|
+
- Use memoization for reusable Three.js objects.
|
|
33
|
+
- Prefer visibility toggles over frequent mount/unmount churn.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
|
|
37
|
+
```jsx
|
|
38
|
+
const geometry = useMemo(() => new THREE.BoxGeometry(), []);
|
|
39
|
+
const material = useMemo(() => new THREE.MeshStandardMaterial(), []);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Rendering Policy
|
|
43
|
+
|
|
44
|
+
For mostly static scenes:
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
<Canvas frameloop="demand" />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Trigger renders when external mutations happen:
|
|
51
|
+
|
|
52
|
+
```jsx
|
|
53
|
+
const { invalidate } = useThree();
|
|
54
|
+
invalidate();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Attach controls change events to invalidate when needed.
|
|
58
|
+
|
|
59
|
+
## Adaptive Quality
|
|
60
|
+
|
|
61
|
+
Use `PerformanceMonitor` or equivalent to adapt quality:
|
|
62
|
+
|
|
63
|
+
- Lower DPR on decline.
|
|
64
|
+
- Disable expensive postprocessing first.
|
|
65
|
+
- Apply fallback quality profile if repeated flip-flops occur.
|
|
66
|
+
|
|
67
|
+
Use transitions for expensive UI state updates:
|
|
68
|
+
|
|
69
|
+
```jsx
|
|
70
|
+
const [isPending, startTransition] = useTransition();
|
|
71
|
+
startTransition(() => {
|
|
72
|
+
setHeavyState(next);
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Strict Anti-Patterns
|
|
77
|
+
|
|
78
|
+
Avoid these patterns:
|
|
79
|
+
|
|
80
|
+
- `setState` inside `useFrame`.
|
|
81
|
+
- Per-frame object allocation (`new Vector3()` in hot path).
|
|
82
|
+
- Constant remounting of heavy 3D subtrees.
|
|
83
|
+
- Fast pointer-driven transforms through React state.
|
|
84
|
+
- Unbounded postprocessing and DPR at all times.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Renderer and Loop Patterns
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- Renderer Selection
|
|
6
|
+
- WebGPU/WebGL Initialization
|
|
7
|
+
- Render Loop Patterns
|
|
8
|
+
- On-Demand Rendering
|
|
9
|
+
- Adaptive DPR
|
|
10
|
+
- Renderer Flags
|
|
11
|
+
|
|
12
|
+
## Renderer Selection
|
|
13
|
+
|
|
14
|
+
Use a profile-first selection strategy:
|
|
15
|
+
|
|
16
|
+
- Prefer `WebGPURenderer` for new work when available.
|
|
17
|
+
- Rely on fallback behavior when WebGPU is unavailable.
|
|
18
|
+
- Keep compatibility checks for advanced features.
|
|
19
|
+
|
|
20
|
+
Do not use deprecated renderer methods in new guidance.
|
|
21
|
+
|
|
22
|
+
## WebGPU/WebGL Initialization
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { WebGPURenderer } from "three/webgpu";
|
|
26
|
+
|
|
27
|
+
const renderer = new WebGPURenderer({
|
|
28
|
+
antialias: false,
|
|
29
|
+
powerPreference: "high-performance",
|
|
30
|
+
});
|
|
31
|
+
await renderer.init();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use `forceWebGL: true` only for explicit fallback testing or compatibility debugging.
|
|
35
|
+
|
|
36
|
+
## Render Loop Patterns
|
|
37
|
+
|
|
38
|
+
Prefer `setAnimationLoop` for continuous rendering cases:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
renderer.setAnimationLoop(() => {
|
|
42
|
+
renderer.render(scene, camera);
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Avoid recommending deprecated async render loop APIs.
|
|
47
|
+
|
|
48
|
+
## On-Demand Rendering
|
|
49
|
+
|
|
50
|
+
Use on-demand rendering for mostly static scenes.
|
|
51
|
+
|
|
52
|
+
Vanilla pattern:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
let renderRequested = false;
|
|
56
|
+
|
|
57
|
+
function render() {
|
|
58
|
+
renderRequested = false;
|
|
59
|
+
renderer.render(scene, camera);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function requestRenderIfNotRequested() {
|
|
63
|
+
if (renderRequested) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
renderRequested = true;
|
|
68
|
+
requestAnimationFrame(render);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Hook this to input/resize/loading events.
|
|
73
|
+
|
|
74
|
+
R3F pattern:
|
|
75
|
+
|
|
76
|
+
```jsx
|
|
77
|
+
<Canvas frameloop="demand" />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
const { invalidate } = useThree();
|
|
82
|
+
controls.addEventListener("change", invalidate);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If damping is enabled, request frames through a guard to avoid redundant scheduling.
|
|
86
|
+
|
|
87
|
+
## Adaptive DPR
|
|
88
|
+
|
|
89
|
+
Use adaptive DPR when frame stability drops.
|
|
90
|
+
|
|
91
|
+
R3F example:
|
|
92
|
+
|
|
93
|
+
```jsx
|
|
94
|
+
<PerformanceMonitor
|
|
95
|
+
onIncline={() => setDpr(2)}
|
|
96
|
+
onDecline={() => setDpr(1)}
|
|
97
|
+
flipflops={3}
|
|
98
|
+
onFallback={() => setDpr(1)}
|
|
99
|
+
/>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Treat DPR changes as one tool, not a complete fix.
|
|
103
|
+
|
|
104
|
+
## Renderer Flags
|
|
105
|
+
|
|
106
|
+
Set renderer flags by requirement, not cargo cult defaults:
|
|
107
|
+
|
|
108
|
+
- Disable `alpha` if transparent canvas is not required.
|
|
109
|
+
- Disable `stencil` when unused.
|
|
110
|
+
- Keep `depth` enabled unless the rendering model proves otherwise.
|
|
111
|
+
- Choose antialiasing strategy by profiling native MSAA vs post AA.
|
|
112
|
+
|
|
113
|
+
When using postprocessing, re-check final color-space and tone-mapping pipeline correctness.
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alaagh/brainkit",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Installable npm bundle of AI skills with CLI and JavaScript API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"brainkit": "dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"CHANGELOG.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "node scripts/build.mjs",
|
|
25
|
+
"validate:skills": "node scripts/validate-skills.mjs",
|
|
26
|
+
"test": "npm run build && npm run validate:skills && node --test --test-concurrency=1 tests/*.test.mjs",
|
|
27
|
+
"prepare": "npm run build",
|
|
28
|
+
"prepublishOnly": "npm test"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"ai",
|
|
32
|
+
"skills",
|
|
33
|
+
"threejs",
|
|
34
|
+
"react-three-fiber",
|
|
35
|
+
"webgl",
|
|
36
|
+
"webgpu",
|
|
37
|
+
"performance",
|
|
38
|
+
"llm",
|
|
39
|
+
"codex"
|
|
40
|
+
],
|
|
41
|
+
"author": "Alaa Alghazouli",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/alaa-alghazouli/brainkit.git"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/alaa-alghazouli/brainkit/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/alaa-alghazouli/brainkit#readme",
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|