├── screenshot_export_vanillajs.png ├── public ├── 3d-models │ └── planet │ │ └── scene.splinecode └── vite.svg ├── counter.js ├── .gitignore ├── package.json ├── javascript.svg ├── main.js ├── style.css ├── README.md └── index.html /screenshot_export_vanillajs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandrasch/spline-selfhosted/HEAD/screenshot_export_vanillajs.png -------------------------------------------------------------------------------- /public/3d-models/planet/scene.splinecode: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandrasch/spline-selfhosted/HEAD/public/3d-models/planet/scene.splinecode -------------------------------------------------------------------------------- /counter.js: -------------------------------------------------------------------------------- 1 | export function setupCounter(element) { 2 | let counter = 0 3 | const setCounter = (count) => { 4 | counter = count 5 | element.innerHTML = `count is ${counter}` 6 | } 7 | element.addEventListener('click', () => setCounter(counter + 1)) 8 | setCounter(0) 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spline-selfhosted", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "vite": "^4.4.5" 13 | }, 14 | "dependencies": { 15 | "@splinetool/runtime": "^0.9.493", 16 | "@splinetool/viewer": "^0.9.493" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /javascript.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import './style.css' 4 | 5 | // Import spline scene via vanillajs runtime: 6 | 7 | import { Application } from '@splinetool/runtime'; 8 | 9 | // make sure you have a canvas in the body 10 | const canvas = document.getElementById('canvas3d'); 11 | 12 | // start the application and load the scene (selfhosted) 13 | const spline = new Application(canvas); 14 | spline.load('/3d-models/planet/scene.splinecode'); 15 | 16 | // TODO: does not work? 17 | spline.setBackgroundColor('red'); 18 | 19 | // import spline viewer webcomponent 20 | import { SplineViewer } from '@splinetool/viewer'; 21 | 22 | // Vite scaffold demo code: 23 | // import javascriptLogo from './javascript.svg' 24 | // import viteLogo from '/vite.svg' 25 | // import { setupCounter } from './counter.js' 26 | 27 | // document.querySelector('#app .vite-demo-content').innerHTML = ` 28 | //
29 | // 30 | // 31 | // 32 | // 33 | // 34 | // 35 | //

Hello Vite!

36 | //
37 | // 38 | //
39 | //

40 | // Click on the Vite logo to learn more 41 | //

42 | //
43 | // ` 44 | 45 | // setupCounter(document.querySelector('#counter')) 46 | 47 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, 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 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | #app { 40 | max-width: 1280px; 41 | margin: 0 auto; 42 | padding: 2rem; 43 | text-align: center; 44 | } 45 | 46 | .logo { 47 | height: 6em; 48 | padding: 1.5em; 49 | will-change: filter; 50 | transition: filter 300ms; 51 | } 52 | .logo:hover { 53 | filter: drop-shadow(0 0 2em #646cffaa); 54 | } 55 | .logo.vanilla:hover { 56 | filter: drop-shadow(0 0 2em #f7df1eaa); 57 | } 58 | 59 | .card { 60 | padding: 2em; 61 | } 62 | 63 | .read-the-docs { 64 | color: #888; 65 | } 66 | 67 | button { 68 | border-radius: 8px; 69 | border: 1px solid transparent; 70 | padding: 0.6em 1.2em; 71 | font-size: 1em; 72 | font-weight: 500; 73 | font-family: inherit; 74 | background-color: #1a1a1a; 75 | cursor: pointer; 76 | transition: border-color 0.25s; 77 | } 78 | button:hover { 79 | border-color: #646cff; 80 | } 81 | button:focus, 82 | button:focus-visible { 83 | outline: 4px auto -webkit-focus-ring-color; 84 | } 85 | 86 | @media (prefers-color-scheme: light) { 87 | :root { 88 | color: #213547; 89 | background-color: #ffffff; 90 | } 91 | a:hover { 92 | color: #747bff; 93 | } 94 | button { 95 | background-color: #f9f9f9; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spline-selfhosted 2 | 3 | - demo repository to show selfhosting of spline 3D scene (exports) 4 | 5 | Two approaches used: 6 | 7 | - vanillajs runtime (https://www.npmjs.com/package/@splinetool/runtime) 8 | - spline-viewer web component (https://www.npmjs.com/package/@splinetool/viewer) 9 | 10 | Demo: 11 | 12 | - https://spline-selfhosted.vercel.app/ ([GDPR check](https://webbkoll.dataskydd.net/de/results?url=http%3A%2F%2Fspline-selfhosted.vercel.app%2F)) 13 | 14 | ## Goals 15 | 16 | - [x] GDPR-compatibility (no 3rd party connections / no cookies), checked with https://webbkoll.dataskydd.net/de/results?url=http%3A%2F%2Fspline-selfhosted.vercel.app%2F 17 | - [ ] vanillajs: transparent background -> setBackgroundColor()? 18 | - [ ] vanillajs: Follow mouse cursor 19 | - [ ] vanillajs: Use click events --> Handling events outside of Spline (https://docs.spline.design/77c32288501a479fa8bc5e787f1e0878) 20 | - [ ] vanillajs: Use preload? https://www.npmjs.com/package/@splinetool/runtime#preloading-your-scene 21 | 22 | ## Local setup 23 | 24 | ``` 25 | npm install 26 | npm run dev 27 | ``` 28 | 29 | ## How was this created? 30 | 31 | Spline offers several ways to export code, see: [Exporting as Code 32 | ](https://docs.spline.design/77c32288501a479fa8bc5e787f1e0878). There is also a Web Component available: 33 | [spline-viewer](https://viewer.spline.design/). 34 | 35 | First, we scaffold a new vite project with vanillajs: 36 | 37 | ```bash 38 | # https://vitejs.dev/guide/#scaffolding-your-first-vite-project 39 | npm create vite@latest . -- --template vanilla 40 | ``` 41 | 42 | ### spline runtime (vanillajs) 43 | 44 | 1. Install it: 45 | 46 | 47 | ```bash 48 | # https://www.npmjs.com/package/@splinetool/runtime 49 | npm install @splinetool/runtime 50 | ``` 51 | 52 | 2. Export spline scene 53 | 54 | Use "Download bundled zip (Web content)" here: 55 | 56 | ![screenshot spline export vanillajs dialog](screenshot_export_vanillajs.png) 57 | 58 | Unzip it and place the `scene.splinecode` file into `public/3d-models`. 59 | 60 | 3. Add `` to index.html, add spline JS code to main.js 61 | 62 | Instead of loading the scene from the spline server, we use the local file: 63 | 64 | ```js 65 | spline.load('/3d-models/planet/scene.splinecode'); 66 | ``` 67 | 68 | 69 | ### spline-viewer web component 70 | 71 | 1. Install it: 72 | ```bash 73 | # https://www.npmjs.com/package/@splinetool/viewer 74 | npm install @splinetool/viewer 75 | ``` 76 | 77 | 2. Import it in main.js: 78 | 79 | ```js 80 | // import spline-viewer web component 81 | import { SplineViewer } from '@splinetool/viewer'; 82 | ``` 83 | 84 | 3. Just use the exported spline scene (see above) in HTML: 85 | 86 | ``` 87 | 88 | ``` 89 | 90 | ## Credits 91 | 92 | - 3D Model example from spline: Planet (https://spline.design/examples) 93 | 94 | ### Resources 95 | 96 | - Spline runtime docs: https://docs.spline.design/77c32288501a479fa8bc5e787f1e0878 97 | - Spline viewer docs: https://docs.spline.design/67b4c8ec0d2b46dd8588a99a7e94db6e -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spline selfhosted 8 | 9 | 10 |
11 | 12 |

Hello, spline!

13 | 14 |

The exported spline 3D scenes are loaded directly from this webserver, not from the spline server.
Therefore no extra cookie / privacy notice is needed for the 3D scenes (GDPR).

15 | 16 |

spline runtime (vanillajs)

17 |

https://www.npmjs.com/package/@splinetool/runtime

18 |
19 | 20 |
21 | 22 |

spline-viewer (web component)

23 |

https://www.npmjs.com/package/@splinetool/viewer, https://viewer.spline.design/

24 | 25 |
26 | 27 |
28 | 29 |

This website is just for demonstrational purposes. Imprint & Privacy

30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | --------------------------------------------------------------------------------