create-playcanvas 0.2.6 → 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.
Files changed (103) hide show
  1. package/README.md +51 -47
  2. package/dist/index.mjs +49 -46
  3. package/package.json +54 -49
  4. package/templates/_shared/model-viewer/public/playcanvas-cube.glb +0 -0
  5. package/templates/engine/base/README.md +38 -0
  6. package/templates/engine/base/eslint.config.mjs +3 -0
  7. package/templates/engine/base/package.json +30 -0
  8. package/templates/engine/base/prettier.config.mjs +1 -0
  9. package/templates/engine/base/src/vite-env.d.ts +19 -0
  10. package/templates/engine/base/tsconfig.json +25 -0
  11. package/templates/engine/interactive-sphere/index.html +13 -0
  12. package/templates/engine/interactive-sphere/src/App.ts +180 -0
  13. package/templates/{vanilla-ts → engine/interactive-sphere}/src/main.ts +8 -8
  14. package/templates/engine/interactive-sphere/src/style.css +107 -0
  15. package/templates/engine/interactive-sphere/src/utils.ts +16 -0
  16. package/templates/engine/model-viewer/index.html +13 -0
  17. package/templates/engine/model-viewer/src/main.ts +81 -0
  18. package/templates/engine/model-viewer/src/style.css +8 -0
  19. package/templates/engine/spinning-cube/index.html +13 -0
  20. package/templates/engine/spinning-cube/src/main.ts +65 -0
  21. package/templates/engine/spinning-cube/src/style.css +8 -0
  22. package/templates/engine/splat-viewer/index.html +13 -0
  23. package/templates/engine/splat-viewer/src/main.ts +74 -0
  24. package/templates/engine/splat-viewer/src/style.css +8 -0
  25. package/templates/index.ts +47 -57
  26. package/templates/react/base/README.md +38 -0
  27. package/templates/react/base/eslint.config.mjs +14 -0
  28. package/templates/react/base/package.json +36 -0
  29. package/templates/react/base/prettier.config.mjs +1 -0
  30. package/templates/react/base/src/vite-env.d.ts +19 -0
  31. package/templates/react/base/tsconfig.json +26 -0
  32. package/templates/react/base/vite.config.ts +7 -0
  33. package/templates/react/interactive-sphere/index.html +13 -0
  34. package/templates/react/interactive-sphere/src/App.css +75 -0
  35. package/templates/react/interactive-sphere/src/App.tsx +43 -0
  36. package/templates/react/interactive-sphere/src/Scene.tsx +69 -0
  37. package/templates/react/interactive-sphere/src/index.css +51 -0
  38. package/templates/react/interactive-sphere/src/main.tsx +11 -0
  39. package/templates/react/model-viewer/index.html +13 -0
  40. package/templates/react/model-viewer/src/App.tsx +43 -0
  41. package/templates/react/model-viewer/src/index.css +9 -0
  42. package/templates/react/model-viewer/src/main.tsx +11 -0
  43. package/templates/react/spinning-cube/index.html +13 -0
  44. package/templates/react/spinning-cube/src/App.tsx +36 -0
  45. package/templates/react/spinning-cube/src/index.css +9 -0
  46. package/templates/react/spinning-cube/src/main.tsx +11 -0
  47. package/templates/react/splat-viewer/index.html +13 -0
  48. package/templates/react/splat-viewer/src/App.tsx +39 -0
  49. package/templates/react/splat-viewer/src/index.css +9 -0
  50. package/templates/react/splat-viewer/src/main.tsx +11 -0
  51. package/templates/web-components/base/README.md +66 -0
  52. package/templates/web-components/base/_gitignore +24 -0
  53. package/templates/web-components/base/eslint.config.mjs +3 -0
  54. package/templates/web-components/base/package.json +31 -0
  55. package/templates/web-components/base/prettier.config.mjs +1 -0
  56. package/templates/web-components/base/src/assets/playcanvas.png +0 -0
  57. package/templates/web-components/base/src/vite-env.d.ts +19 -0
  58. package/templates/web-components/base/tsconfig.json +25 -0
  59. package/templates/web-components/interactive-sphere/index.html +53 -0
  60. package/templates/web-components/interactive-sphere/src/assets/typescript.svg +1 -0
  61. package/templates/web-components/interactive-sphere/src/main.ts +76 -0
  62. package/templates/web-components/interactive-sphere/src/style.css +111 -0
  63. package/templates/web-components/model-viewer/index.html +34 -0
  64. package/templates/web-components/model-viewer/src/main.ts +26 -0
  65. package/templates/web-components/model-viewer/src/style.css +8 -0
  66. package/templates/web-components/spinning-cube/index.html +29 -0
  67. package/templates/web-components/spinning-cube/src/main.ts +20 -0
  68. package/templates/web-components/spinning-cube/src/style.css +8 -0
  69. package/templates/web-components/splat-viewer/index.html +31 -0
  70. package/templates/web-components/splat-viewer/src/main.ts +14 -0
  71. package/templates/web-components/splat-viewer/src/style.css +8 -0
  72. package/index.js +0 -3
  73. package/templates/react-ts/README.md +0 -82
  74. package/templates/react-ts/index.html +0 -13
  75. package/templates/react-ts/package.json +0 -29
  76. package/templates/react-ts/scripts/ensure-node-version.cjs +0 -12
  77. package/templates/react-ts/src/App.css +0 -87
  78. package/templates/react-ts/src/App.tsx +0 -46
  79. package/templates/react-ts/src/Scene.tsx +0 -70
  80. package/templates/react-ts/src/index.css +0 -73
  81. package/templates/react-ts/src/main.tsx +0 -10
  82. package/templates/react-ts/src/vite-env.d.ts +0 -1
  83. package/templates/react-ts/tsconfig.app.json +0 -27
  84. package/templates/react-ts/tsconfig.json +0 -7
  85. package/templates/react-ts/tsconfig.node.json +0 -25
  86. package/templates/react-ts/vite.config.ts +0 -7
  87. package/templates/vanilla-ts/README.md +0 -114
  88. package/templates/vanilla-ts/index.html +0 -13
  89. package/templates/vanilla-ts/package.json +0 -23
  90. package/templates/vanilla-ts/public/environment-map.png +0 -0
  91. package/templates/vanilla-ts/scripts/ensure-node-version.cjs +0 -12
  92. package/templates/vanilla-ts/src/App.ts +0 -182
  93. package/templates/vanilla-ts/src/style.css +0 -140
  94. package/templates/vanilla-ts/src/utils.ts +0 -16
  95. package/templates/vanilla-ts/src/vite-env.d.ts +0 -1
  96. package/templates/vanilla-ts/tsconfig.json +0 -25
  97. /package/templates/{react-ts → _shared/interactive-sphere}/public/environment-map.png +0 -0
  98. /package/templates/{react-ts → engine/base}/_gitignore +0 -0
  99. /package/templates/{react-ts → engine/base}/src/assets/playcanvas.png +0 -0
  100. /package/templates/{vanilla-ts → engine/interactive-sphere}/src/assets/typescript.svg +0 -0
  101. /package/templates/{vanilla-ts → react/base}/_gitignore +0 -0
  102. /package/templates/{vanilla-ts → react/base}/src/assets/playcanvas.png +0 -0
  103. /package/templates/{react-ts → react/interactive-sphere}/src/assets/react.svg +0 -0
@@ -0,0 +1,8 @@
1
+ body {
2
+ margin: 0;
3
+ overflow: hidden;
4
+ }
5
+
6
+ canvas {
7
+ display: block;
8
+ }
@@ -1,60 +1,50 @@
1
- import colors, { yellow } from 'picocolors'
2
- const { blue, cyan } = colors
1
+ import colors from 'picocolors';
3
2
 
4
- /**
5
- * You can add new frameworks here.
6
- *
7
- * The framework name is used to create the directory name.
8
- * The framework display name is used to display the framework name in the CLI.
9
- * The framework description is used to display the framework description in the CLI.
10
- * The framework color is used to display the framework color in the CLI.
11
- * The framework variants are used to display the framework variants in the CLI.
12
- *
13
- */
14
- export const FRAMEWORKS: Framework[] = [
15
- {
16
- name: 'vanilla',
17
- display: 'Vanilla',
18
- description: 'with playcanvas engine',
19
- color: yellow,
20
- variants: [
21
- {
22
- name: 'vanilla-ts',
23
- display: 'TypeScript',
24
- color: blue,
25
- },
26
- ],
27
- },
28
- {
29
- name: 'react',
30
- display: 'React',
31
- description: 'with @playcanvas/react',
32
- color: cyan,
33
- variants: [
34
- {
35
- name: 'react-ts',
36
- display: 'TypeScript',
37
- color: blue,
38
- },
39
- ],
40
- }
41
- ]
3
+ const { cyan, magenta, yellow } = colors;
42
4
 
43
- export const TEMPLATES = FRAMEWORKS.flatMap((f) => f.variants.map((v) => v.name))
5
+ export const TEMPLATES = [
6
+ {
7
+ name: 'engine',
8
+ display: 'Engine',
9
+ description: 'with the PlayCanvas Engine API directly',
10
+ color: yellow
11
+ },
12
+ {
13
+ name: 'react',
14
+ display: 'React',
15
+ description: 'with @playcanvas/react',
16
+ color: cyan
17
+ },
18
+ {
19
+ name: 'web-components',
20
+ display: 'Web Components',
21
+ description: 'with @playcanvas/web-components',
22
+ color: magenta
23
+ }
24
+ ];
44
25
 
45
- export type ColorFunc = (str: string | number) => string
46
-
47
- export type FrameworkVariant = {
48
- name: string
49
- display: string
50
- color: ColorFunc
51
- customCommand?: string
52
- }
53
-
54
- export type Framework = {
55
- name: string
56
- display: string
57
- color: ColorFunc
58
- description?: string
59
- variants: FrameworkVariant[]
60
- }
26
+ // Every template holds a `base` directory of shared tooling plus one directory per boilerplate,
27
+ // which is copied over the top. Assets a boilerplate needs in every template live once in
28
+ // `_shared/<boilerplate>` and are seeded in first. The first entry is the default.
29
+ export const BOILERPLATES = [
30
+ {
31
+ name: 'spinning-cube',
32
+ display: 'Spinning Cube',
33
+ description: 'a rotating cube and a light'
34
+ },
35
+ {
36
+ name: 'interactive-sphere',
37
+ display: 'Interactive Sphere',
38
+ description: 'orbit controls, a grid and pointer picking'
39
+ },
40
+ {
41
+ name: 'model-viewer',
42
+ display: 'Model Viewer',
43
+ description: 'a glTF model, orbit controls and a procedural sky'
44
+ },
45
+ {
46
+ name: 'splat-viewer',
47
+ display: 'Splat Viewer',
48
+ description: 'a Gaussian splat and orbit controls'
49
+ }
50
+ ];
@@ -0,0 +1,38 @@
1
+ # PlayCanvas React + TypeScript Starter
2
+
3
+ A Vite-powered `@playcanvas/react` project with TypeScript, hot module replacement, ESLint and Prettier.
4
+
5
+ ## Prerequisites
6
+
7
+ Node.js 22.23.2 or later.
8
+
9
+ ## Getting started
10
+
11
+ ```bash
12
+ npm create playcanvas@latest playcanvas-project -- --template react
13
+ cd playcanvas-project
14
+ npm install
15
+ npm run dev
16
+ ```
17
+
18
+ Open <http://localhost:5173>. Edit the files under `src/` and save to see the scene update.
19
+
20
+ ## Scripts
21
+
22
+ | Command | Description |
23
+ | ------------------- | --------------------------------- |
24
+ | `npm run dev` | Start the Vite development server |
25
+ | `npm run build` | Build for production |
26
+ | `npm run start` | Preview the production build |
27
+ | `npm run lint` | Run ESLint |
28
+ | `npm run fmt` | Check formatting |
29
+ | `npm run typecheck` | Run TypeScript checks |
30
+
31
+ Run `npm run build` to generate a deployable static site in `dist/`.
32
+
33
+ ## Further reading
34
+
35
+ - [PlayCanvas React manual](https://developer.playcanvas.com/user-manual/react/)
36
+ - [React documentation](https://react.dev/)
37
+ - [Vite documentation](https://vite.dev/)
38
+ - [TypeScript documentation](https://www.typescriptlang.org/docs/)
@@ -0,0 +1,14 @@
1
+ import reactConfig from '@playcanvas/eslint-config/react';
2
+ import typescriptConfig from '@playcanvas/eslint-config/typescript';
3
+
4
+ export default [
5
+ ...typescriptConfig,
6
+ ...reactConfig,
7
+ {
8
+ rules: {
9
+ 'react/jsx-uses-react': 'off',
10
+ 'react/prop-types': 'off',
11
+ 'react/react-in-jsx-scope': 'off'
12
+ }
13
+ }
14
+ ];
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "playcanvas-project",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "description": "A PlayCanvas React project",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "vite build",
9
+ "dev": "vite",
10
+ "fmt": "prettier --check .",
11
+ "fmt:fix": "prettier --write .",
12
+ "lint": "eslint .",
13
+ "lint:fix": "eslint . --fix",
14
+ "start": "vite preview",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@playcanvas/react": "^0.11.5",
19
+ "playcanvas": "^2.21.3",
20
+ "react": "^19.2.8",
21
+ "react-dom": "^19.2.8"
22
+ },
23
+ "devDependencies": {
24
+ "@playcanvas/eslint-config": "3.0.0-beta.8",
25
+ "@types/react": "^19.2.18",
26
+ "@types/react-dom": "^19.2.4",
27
+ "@vitejs/plugin-react": "^6.0.5",
28
+ "eslint": "^9.39.5",
29
+ "prettier": "^3.9.6",
30
+ "typescript": "~6.0.3",
31
+ "vite": "^8.2.0"
32
+ },
33
+ "engines": {
34
+ "node": ">=22.23.2"
35
+ }
36
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@playcanvas/eslint-config/prettier';
@@ -0,0 +1,19 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ declare module 'playcanvas/scripts/esm/camera-controls.mjs' {
4
+ import type { Script } from 'playcanvas';
5
+
6
+ export const CameraControls: typeof Script & (new () => Script);
7
+ }
8
+
9
+ declare module 'playcanvas/scripts/esm/grid.mjs' {
10
+ import type { Script } from 'playcanvas';
11
+
12
+ export const Grid: typeof Script & (new () => Script);
13
+ }
14
+
15
+ declare module 'playcanvas/scripts/esm/sky/procedural-sky.mjs' {
16
+ import type { Script } from 'playcanvas';
17
+
18
+ export const ProceduralSky: typeof Script & (new () => Script);
19
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["src", "vite.config.ts"]
26
+ }
@@ -0,0 +1,7 @@
1
+ import react from '@vitejs/plugin-react';
2
+ import { defineConfig } from 'vite';
3
+
4
+ // https://vite.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()]
7
+ });
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>PlayCanvas + React + TS</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,75 @@
1
+ #root {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100vh;
5
+ width: 100vw;
6
+ }
7
+
8
+ header {
9
+ display: flex;
10
+ flex-direction: row;
11
+ align-items: center;
12
+ padding: 2rem;
13
+ }
14
+
15
+ .absolute {
16
+ position: absolute;
17
+ top: 0;
18
+ left: 0;
19
+ }
20
+
21
+ .full-bleed {
22
+ width: 100vw;
23
+ height: 100vh;
24
+ overflow: hidden;
25
+ }
26
+
27
+ .logo {
28
+ height: 2em;
29
+ padding: 0.5em;
30
+ will-change: filter;
31
+ transition: filter 300ms;
32
+ }
33
+
34
+ .playcanvas-logo {
35
+ border-radius: 100%;
36
+ }
37
+
38
+ .logo:hover {
39
+ filter: drop-shadow(0 0 2em #646cffaa);
40
+ }
41
+
42
+ .logo.react:hover {
43
+ filter: drop-shadow(0 0 2em #61dafbaa);
44
+ }
45
+
46
+ .read-the-docs {
47
+ color: #888;
48
+ }
49
+
50
+ .overlay {
51
+ margin: 0 auto;
52
+ text-align: center;
53
+ display: flex;
54
+ flex-direction: column;
55
+ align-items: center;
56
+ justify-content: stretch;
57
+ width: 100vw;
58
+ height: 100vh;
59
+ pointer-events: none;
60
+ }
61
+
62
+ .overlay a {
63
+ pointer-events: auto;
64
+ }
65
+
66
+ .overlay > .grow {
67
+ flex-grow: 1;
68
+ }
69
+
70
+ .pill {
71
+ background-color: #000;
72
+ color: #fff;
73
+ padding: 0.5rem 1rem;
74
+ border-radius: 0.5rem;
75
+ }
@@ -0,0 +1,43 @@
1
+ import { Application } from '@playcanvas/react';
2
+ import { useState } from 'react';
3
+
4
+ import playcanvasLogo from './assets/playcanvas.png';
5
+ import reactLogo from './assets/react.svg';
6
+ import './App.css';
7
+ import Scene from './Scene';
8
+
9
+ function App() {
10
+ const [count, setCount] = useState(0);
11
+
12
+ return (
13
+ <>
14
+ <div className="full-bleed">
15
+ <Application className="playcanvas-app">
16
+ <Scene onClick={() => setCount((count) => count + 1)} />
17
+ </Application>
18
+ </div>
19
+ <div className="absolute overlay">
20
+ <div className="grow">
21
+ <header>
22
+ <h1>PlayCanvas + React</h1>
23
+ <a href="https://developer.playcanvas.com/user-manual/react/" target="_blank" rel="noreferrer">
24
+ <img src={playcanvasLogo} className="playcanvas-logo logo" alt="PlayCanvas logo" />
25
+ </a>
26
+ <a href="https://react.dev" target="_blank" rel="noreferrer">
27
+ <img src={reactLogo} className="logo react" alt="React logo" />
28
+ </a>
29
+ </header>
30
+ </div>
31
+ <div>
32
+ <span className="pill">Click Count: {count}</span>
33
+ <p>
34
+ Edit <code>src/Scene.tsx</code> and save to test HMR
35
+ </p>
36
+ </div>
37
+ <p className="read-the-docs">Click on the PlayCanvas and React logos to learn more</p>
38
+ </div>
39
+ </>
40
+ );
41
+ }
42
+
43
+ export default App;
@@ -0,0 +1,69 @@
1
+ import { Entity } from '@playcanvas/react';
2
+ import { Camera, Environment, Render, Script } from '@playcanvas/react/components';
3
+ import { useEnvAtlas, useMaterial } from '@playcanvas/react/hooks';
4
+ import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
5
+ import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
6
+ import { useEffect, useState } from 'react';
7
+
8
+ /**
9
+ * The Scene renders a sphere with a grid and camera controls
10
+ */
11
+ function Scene({ onClick }: SceneProps) {
12
+ const [hovering, setHovering] = useState(false);
13
+
14
+ // Set a material color based on the hover state
15
+ const diffuse = hovering ? 'orange' : 'lightgrey';
16
+
17
+ // Create a material for the sphere
18
+ const material = useMaterial({ diffuse });
19
+
20
+ // Load the environment map
21
+ const { asset: envMap } = useEnvAtlas('/environment-map.png');
22
+
23
+ // Change the mouse cursor based on the hover state
24
+ useEffect(() => {
25
+ document.body.style.cursor = hovering ? 'pointer' : 'default';
26
+
27
+ return () => {
28
+ document.body.style.cursor = '';
29
+ };
30
+ }, [hovering]);
31
+
32
+ // Don't render until the environment map is loaded
33
+ if (!envMap) return null;
34
+
35
+ return (
36
+ <>
37
+ {/* Render some environment lighting using the environment map */}
38
+ <Environment envAtlas={envMap} showSkybox={false} />
39
+
40
+ {/* Render a background grid */}
41
+ <Entity scale={[1000, 1000, 1000]}>
42
+ <Script script={Grid} />
43
+ </Entity>
44
+
45
+ {/* Create a camera entity with camera controls */}
46
+ <Entity name="camera" position={[4, 1, 4]}>
47
+ <Camera clearColor="#171717" />
48
+ <Script script={CameraControls} />
49
+ </Entity>
50
+
51
+ {/* Create and position entity with pointer events */}
52
+ <Entity
53
+ position={[0, 0.5, 0]}
54
+ onClick={onClick}
55
+ onPointerOver={() => setHovering(true)}
56
+ onPointerOut={() => setHovering(false)}
57
+ >
58
+ {/* Render a sphere with the material */}
59
+ <Render type="sphere" material={material} />
60
+ </Entity>
61
+ </>
62
+ );
63
+ }
64
+
65
+ type SceneProps = {
66
+ onClick: () => void;
67
+ };
68
+
69
+ export default Scene;
@@ -0,0 +1,51 @@
1
+ :root {
2
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ a {
17
+ font-weight: 500;
18
+ color: #646cff;
19
+ text-decoration: inherit;
20
+ }
21
+ a:hover {
22
+ color: #535bf2;
23
+ }
24
+
25
+ a > img {
26
+ vertical-align: middle;
27
+ }
28
+
29
+ body {
30
+ margin: 0;
31
+ display: flex;
32
+ place-items: center;
33
+ min-width: 320px;
34
+ min-height: 100vh;
35
+ }
36
+
37
+ h1 {
38
+ font-size: 2.4em;
39
+ line-height: 1.1;
40
+ opacity: 0.85;
41
+ }
42
+
43
+ @media (prefers-color-scheme: light) {
44
+ :root {
45
+ color: #213547;
46
+ background-color: #ffffff;
47
+ }
48
+ a:hover {
49
+ color: #747bff;
50
+ }
51
+ }
@@ -0,0 +1,11 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+
4
+ import './index.css';
5
+ import App from './App.tsx';
6
+
7
+ createRoot(document.getElementById('root')!).render(
8
+ <StrictMode>
9
+ <App />
10
+ </StrictMode>
11
+ );
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>PlayCanvas + React</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,43 @@
1
+ import { Application, Container, Entity } from '@playcanvas/react';
2
+ import { Camera, Script } from '@playcanvas/react/components';
3
+ import { useModel } from '@playcanvas/react/hooks';
4
+ import { TONEMAP_ACES2 } from 'playcanvas';
5
+ import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
6
+ import { ProceduralSky } from 'playcanvas/scripts/esm/sky/procedural-sky.mjs';
7
+
8
+ // Swap this for your own model - anything you drop in public/ is served from the site root
9
+ const MODEL_URL = '/playcanvas-cube.glb';
10
+
11
+ function Model() {
12
+ const { asset } = useModel(MODEL_URL);
13
+
14
+ // Don't render until the model is loaded
15
+ if (!asset) return null;
16
+
17
+ return <Container asset={asset} />;
18
+ }
19
+
20
+ function App() {
21
+ return (
22
+ <Application>
23
+ {/* The procedural sky is both the background and the image-based lighting, so no
24
+ light or environment map asset is needed. Its default luminance is scene-scale
25
+ bright, so turn it down to keep the model out of clipping */}
26
+ <Entity name="sky">
27
+ <Script script={ProceduralSky} luminance={0.2} />
28
+ </Entity>
29
+
30
+ {/* Create a camera entity with orbit controls. The sky is HDR, so it needs tone
31
+ mapping to bring it back into displayable range - without it the whole image blows
32
+ out to white */}
33
+ <Entity name="camera" position={[0, 0, 3]}>
34
+ <Camera toneMapping={TONEMAP_ACES2} />
35
+ <Script script={CameraControls} />
36
+ </Entity>
37
+
38
+ <Model />
39
+ </Application>
40
+ );
41
+ }
42
+
43
+ export default App;
@@ -0,0 +1,9 @@
1
+ body {
2
+ margin: 0;
3
+ overflow: hidden;
4
+ }
5
+
6
+ #root {
7
+ width: 100vw;
8
+ height: 100vh;
9
+ }
@@ -0,0 +1,11 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+
4
+ import './index.css';
5
+ import App from './App.tsx';
6
+
7
+ createRoot(document.getElementById('root')!).render(
8
+ <StrictMode>
9
+ <App />
10
+ </StrictMode>
11
+ );
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>PlayCanvas + React</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,36 @@
1
+ import { Application, Entity } from '@playcanvas/react';
2
+ import { Camera, Light, Render } from '@playcanvas/react/components';
3
+ import { useAppEvent } from '@playcanvas/react/hooks';
4
+ import type { Entity as PcEntity } from 'playcanvas';
5
+ import { useRef } from 'react';
6
+
7
+ function Scene() {
8
+ const cube = useRef<PcEntity>(null);
9
+
10
+ // Rotate the cube according to the delta time since the last frame
11
+ useAppEvent('update', (dt: number) => cube.current?.rotate(10 * dt, 20 * dt, 30 * dt));
12
+
13
+ return (
14
+ <>
15
+ <Entity name="camera" position={[0, 0, 3]}>
16
+ <Camera clearColor="#8099e6" />
17
+ </Entity>
18
+ <Entity name="light" rotation={[45, 0, 0]}>
19
+ <Light type="directional" />
20
+ </Entity>
21
+ <Entity name="cube" ref={cube}>
22
+ <Render type="box" />
23
+ </Entity>
24
+ </>
25
+ );
26
+ }
27
+
28
+ function App() {
29
+ return (
30
+ <Application>
31
+ <Scene />
32
+ </Application>
33
+ );
34
+ }
35
+
36
+ export default App;
@@ -0,0 +1,9 @@
1
+ body {
2
+ margin: 0;
3
+ overflow: hidden;
4
+ }
5
+
6
+ #root {
7
+ width: 100vw;
8
+ height: 100vh;
9
+ }