feat: Add comprehensive iconography system and enhance brand guidelines
- Add complete Iconography section with Coolicons v4.1 integration - Implement theme-adaptive icons (black for light mode, white for dark mode) - Add Visual Aid modal dialog for accessibility settings - Replace theme toggle with semantic moon/sun icons - Add personality trait icons with appropriate semantic choices - Fix code block theming to respect light/dark mode toggle - Include comprehensive icon categories: Interface, File/Data, Communication, Navigation - Add detailed implementation guides for HTML, SVG, and Tailwind - Create accessibility-aware color system with vision deficiency support - Add Inconsolata and Inter Tight fonts for complete typography system 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
BIN
brand-assets/logos/chorus-landscape-on-blue.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
brand-assets/logos/chorus-landscape-on-grey.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
brand-assets/logos/chorus-landscape-on-mulberry.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
brand-assets/logos/chorus-landscape-on-white.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
@@ -113,7 +113,7 @@ Each component logo follows the pattern: **Component Icon + CHORUS Brand Mark**
|
||||
- Position: Baseline aligned with bottom of CHORUS, or centered vertically
|
||||
|
||||
### Color Values
|
||||
```css
|
||||
css
|
||||
/* Primary Brand Colors */
|
||||
--logo-primary-text: #F5F5DC; /* Natural Paper */
|
||||
--logo-secondary-text: #C0C0C0; /* Brushed Aluminum */
|
||||
@@ -124,7 +124,7 @@ Each component logo follows the pattern: **Component Icon + CHORUS Brand Mark**
|
||||
--logo-primary-text-rev: #000000; /* Carbon Black */
|
||||
--logo-secondary-text-rev: #8B4513; /* Walnut Brown */
|
||||
--logo-background-light: #F5F5DC; /* Natural Paper */
|
||||
```
|
||||
|
||||
|
||||
## Usage Guidelines
|
||||
|
||||
@@ -202,4 +202,4 @@ Each component logo follows the pattern: **Component Icon + CHORUS Brand Mark**
|
||||
- **Social Media**: PNG at platform-specific dimensions
|
||||
- **App Icons**: PNG at required iOS/Android specifications
|
||||
|
||||
This logo concept provides CHORUS Services with a sophisticated, scalable visual identity that reflects the platform's technical capabilities while maintaining the premium, approachable aesthetic required for enterprise clients.
|
||||
This logo concept provides CHORUS Services with a sophisticated, scalable visual identity that reflects the platform's technical capabilities while maintaining the premium, approachable aesthetic required for enterprise clients.
|
||||
|
||||
BIN
brand-assets/logos/chorus-logo.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
brand-assets/logos/chorus-mobius-on-blue.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
brand-assets/logos/chorus-mobius-on-grey.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
brand-assets/logos/chorus-mobius-on-mulberry.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
brand-assets/logos/chorus-mobius-on-white.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
@@ -135,30 +135,30 @@ The CHORUS component logo system creates visual harmony between the main brand a
|
||||
### Combined Logo Layouts
|
||||
|
||||
#### Horizontal Integration
|
||||
```
|
||||
|
||||
[Component Icon] COMPONENT NAME → [CHORUS Icon] CHORUS
|
||||
powered by
|
||||
```
|
||||
|
||||
- **Use Case**: Marketing materials, business cards, letterhead
|
||||
- **Spacing**: Component and CHORUS sections separated by 2x the x-height
|
||||
- **Hierarchy**: Component name 100%, CHORUS name 70%, "powered by" 40%
|
||||
|
||||
#### Vertical Integration
|
||||
```
|
||||
|
||||
[Component Icon]
|
||||
COMPONENT NAME
|
||||
↓
|
||||
[CHORUS Icon]
|
||||
CHORUS Services
|
||||
```
|
||||
|
||||
- **Use Case**: Vertical layouts, mobile applications, square formats
|
||||
- **Spacing**: Consistent vertical rhythm based on typography line-height
|
||||
- **Connection**: Subtle arrow or connection line between icons
|
||||
|
||||
#### Compact Integration
|
||||
```
|
||||
|
||||
[Component Icon][CHORUS Icon] COMPONENT × CHORUS
|
||||
```
|
||||
|
||||
- **Use Case**: Favicons, app icons, tight space applications
|
||||
- **Treatment**: Icons side-by-side with minimal separation
|
||||
- **Typography**: Single line with multiplication symbol (×) or plus (+)
|
||||
@@ -199,7 +199,7 @@ CHORUS Services
|
||||
## Technical Implementation
|
||||
|
||||
### SVG Structure Template
|
||||
```svg
|
||||
svg
|
||||
<svg viewBox="0 0 240 60" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Component Icon -->
|
||||
<g id="component-icon" transform="translate(0,0)">
|
||||
@@ -220,10 +220,10 @@ CHORUS Services
|
||||
<text id="component-name" x="0" y="45" class="component-text">COMPONENT</text>
|
||||
<text id="chorus-name" x="160" y="45" class="chorus-text">CHORUS</text>
|
||||
</svg>
|
||||
```
|
||||
|
||||
|
||||
### CSS Integration Classes
|
||||
```css
|
||||
css
|
||||
.component-logo {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -248,7 +248,7 @@ CHORUS Services
|
||||
font-size: var(--text-size, 18px);
|
||||
color: var(--text-color);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Brand Protection Guidelines
|
||||
|
||||
|
||||
BIN
brand-assets/logos/horizon-gradient.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
238
brand-assets/logos/logo-test.html
Normal file
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>CHORUS</title>
|
||||
<style>
|
||||
body { margin: 0; overflow: hidden; background-color: #5E6367; }
|
||||
canvas { display: block; }
|
||||
</style>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&family=Luckiest+Guy&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
|
||||
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/",
|
||||
"lil-gui": "https://cdn.jsdelivr.net/npm/lil-gui@0.19/+esm"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import GUI from 'lil-gui';
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
||||
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
||||
|
||||
// Initialize CSS2DRenderer
|
||||
const labelRenderer = new CSS2DRenderer();
|
||||
labelRenderer.setSize(window.innerWidth, window.innerHeight);
|
||||
labelRenderer.domElement.style.position = 'absolute';
|
||||
labelRenderer.domElement.style.top = '0px';
|
||||
labelRenderer.domElement.style.pointerEvents = 'none'; // allows clicks to pass through
|
||||
document.body.appendChild(labelRenderer.domElement);
|
||||
|
||||
|
||||
// === Animation parameters ===
|
||||
const spinSpeed = 0.003; // radians per frame
|
||||
|
||||
// Animation parameters (now in an object for GUI control)
|
||||
const params = {
|
||||
spinSpeedX: 0.01, // continuous X spin
|
||||
spinSpeedY: 0.01, // continuous X spin
|
||||
spinSpeedZ: 0.1, // continuous Z spin
|
||||
lightCount: 25,
|
||||
};
|
||||
|
||||
|
||||
let scene, camera, renderer, mobius, clock;
|
||||
let baseRotationY = 0;
|
||||
|
||||
|
||||
init();
|
||||
animate();
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
|
||||
const material = new THREE.MeshPhysicalMaterial({
|
||||
color: 0xFFFFFF,
|
||||
roughness: 0.24,
|
||||
metalness: 1.0,
|
||||
clearcoat: 0.48,
|
||||
clearcoatRoughness: 0.15,
|
||||
reflectivity: 1.2,
|
||||
sheen: 0.35,
|
||||
sheenColor: new THREE.Color(0x212121),
|
||||
sheenRoughness: 0.168,
|
||||
envMapIntensity: 1,
|
||||
});
|
||||
|
||||
// Add scattered lights
|
||||
addRandomLights();
|
||||
|
||||
// Generate a synthetic cube environment
|
||||
const size = 512;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
|
||||
|
||||
// Gradient: sunset horizon (bottom warm, top cool)
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
||||
gradient.addColorStop(0, '#001133'); // top dark blue
|
||||
gradient.addColorStop(0.4, '#223366'); // mid blue
|
||||
gradient.addColorStop(0.5, '#ff8844'); // orange near horizon
|
||||
gradient.addColorStop(0.51, '#000000'); // black horizon
|
||||
gradient.addColorStop(0.8, '#105010'); // green base
|
||||
gradient.addColorStop(1, '#000000'); // black base
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas);
|
||||
texture.mapping = THREE.EquirectangularReflectionMapping;
|
||||
|
||||
// Apply as environment
|
||||
scene.environment = texture;
|
||||
scene.background = null; // keep transparent
|
||||
|
||||
function expandGradient(img) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const w = 512, h = 256; // safe HDRI-like size
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Stretch the 1px-wide strip to fill the canvas
|
||||
ctx.drawImage(img, 0, 0, w, h);
|
||||
|
||||
return new THREE.CanvasTexture(canvas);
|
||||
}
|
||||
|
||||
const envloader = new THREE.ImageLoader();
|
||||
envloader.load('horizon-gradient.png', (image) => {
|
||||
const tex = expandGradient(image);
|
||||
tex.mapping = THREE.EquirectangularReflectionMapping;
|
||||
|
||||
// ✅ safe to feed into PMREM
|
||||
const pmrem = new THREE.PMREMGenerator(renderer);
|
||||
const envMap = pmrem.fromEquirectangular(tex).texture;
|
||||
|
||||
scene.environment = envMap;
|
||||
});
|
||||
|
||||
|
||||
|
||||
const textDiv = document.createElement('div');
|
||||
textDiv.textContent = "CHORUS";
|
||||
textDiv.style.color = "#FFFFFF";
|
||||
textDiv.style.fontSize = "96px";
|
||||
textDiv.style.fontFamily = "Exo, sans-serif";
|
||||
textDiv.style.textAlign = "left";
|
||||
|
||||
// Create CSS2DObject and position it at the origin
|
||||
const textLabel = new CSS2DObject(textDiv);
|
||||
textLabel.position.set(0, 0, 0); // exact origin
|
||||
scene.add(textLabel);
|
||||
|
||||
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 100);
|
||||
camera.position.set(0, 0, 1.8);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
const light = new THREE.PointLight(0xffffff, 1.4);
|
||||
light.position.set(0, 4, 1);
|
||||
scene.add(light);
|
||||
|
||||
const bottomLight = new THREE.PointLight(0x800080, 1.2, 12); // (color, intensity, distance)
|
||||
bottomLight.position.set(0, -4, 1); // directly under the model
|
||||
scene.add(bottomLight);
|
||||
|
||||
const leftLight = new THREE.PointLight(0x808000, 1.45, 5); // (color, intensity, distance)
|
||||
leftLight.position.set(-5, 0, 4); // top left of the model
|
||||
scene.add(leftLight);
|
||||
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.45));
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
|
||||
const dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderPath( 'https://www.gstatic.com/draco/v1/decoders/' );
|
||||
loader.setDRACOLoader( dracoLoader );
|
||||
loader.load(
|
||||
"./mobius-ring.glb", // ensure mobius.glb is in the same folder as this HTML
|
||||
(gltf) => {
|
||||
mobius = gltf.scene;
|
||||
mobius.traverse((child) => {
|
||||
if (child.isMesh) {
|
||||
child.material = material;
|
||||
}
|
||||
});
|
||||
scene.add(mobius);
|
||||
},
|
||||
undefined,
|
||||
(err) => console.error("Error loading model:", err)
|
||||
);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setClearColor(0x000000, 0); // transparent background
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
clock = new THREE.Clock();
|
||||
window.addEventListener("resize", onWindowResize);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
labelRenderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
if (mobius) {
|
||||
const elapsed = clock.getElapsedTime();
|
||||
|
||||
// Continuous spin
|
||||
mobius.rotation.x += params.spinSpeedX;
|
||||
mobius.rotation.y += params.spinSpeedY;
|
||||
mobius.rotation.z += params.spinSpeedZ;
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
labelRenderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function addRandomLights() {
|
||||
for (let i = 0; i < params.lightCount; i++) {
|
||||
const color = new THREE.Color().setHSL(Math.random(), 0.84, 0.9);
|
||||
const light = new THREE.PointLight(color, params.lightIntensity, params.lightDistance);
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const height = (Math.random() - 0.5) * 4;
|
||||
const radius = 6 + Math.random() * 4;
|
||||
light.position.set(Math.cos(angle) * radius, height, Math.sin(angle) * radius);
|
||||
scene.add(light);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -148,7 +148,7 @@ This document provides comprehensive specifications for all CHORUS Services logo
|
||||
## Technical Implementation Specifications
|
||||
|
||||
### SVG Code Structure
|
||||
```svg
|
||||
svg
|
||||
<svg viewBox="0 0 240 60" xmlns="http://www.w3.org/2000/svg" aria-labelledby="chorus-logo-title">
|
||||
<title id="chorus-logo-title">CHORUS Services Logo</title>
|
||||
|
||||
@@ -163,10 +163,10 @@ This document provides comprehensive specifications for all CHORUS Services logo
|
||||
<text x="80" y="50" font-family="-apple-system, SF Pro Text" font-weight="400" font-size="10" fill="#C0C0C0">Services</text>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
|
||||
### CSS Implementation
|
||||
```css
|
||||
css
|
||||
.chorus-logo {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
@@ -194,16 +194,16 @@ This document provides comprehensive specifications for all CHORUS Services logo
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### File Naming Convention
|
||||
```
|
||||
|
||||
chorus-logo-horizontal-color.svg
|
||||
chorus-logo-horizontal-reversed.svg
|
||||
chorus-logo-stacked-color.png
|
||||
chorus-logo-icon-only-white.svg
|
||||
chorus-logo-monochrome-black.pdf
|
||||
```
|
||||
|
||||
|
||||
## Quality Assurance Checklist
|
||||
|
||||
|
||||
245
brand-assets/logos/logo.html
Normal file
@@ -0,0 +1,245 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>CHORUS Möbius Strip</title>
|
||||
<style>
|
||||
body { margin: 0; overflow: hidden; background-color: #0b0213; }
|
||||
canvas { display: block; }
|
||||
</style>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&family=Luckiest+Guy&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
|
||||
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/",
|
||||
"lil-gui": "https://cdn.jsdelivr.net/npm/lil-gui@0.19/+esm"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import GUI from 'lil-gui';
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
||||
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
||||
|
||||
// Initialize CSS2DRenderer
|
||||
const labelRenderer = new CSS2DRenderer();
|
||||
labelRenderer.setSize(window.innerWidth, window.innerHeight);
|
||||
labelRenderer.domElement.style.position = 'absolute';
|
||||
labelRenderer.domElement.style.top = '0px';
|
||||
labelRenderer.domElement.style.pointerEvents = 'none'; // allows clicks to pass through
|
||||
document.body.appendChild(labelRenderer.domElement);
|
||||
|
||||
|
||||
// === Animation parameters ===
|
||||
const spinSpeed = 0.003; // radians per frame
|
||||
|
||||
// Animation parameters (now in an object for GUI control)
|
||||
const params = {
|
||||
spinSpeedX: 0.010, // continuous X spin
|
||||
spinSpeedY: -0.010, // continuous X spin
|
||||
spinSpeedZ: -0.1, // continuous Z spin
|
||||
lightCount: 25,
|
||||
};
|
||||
|
||||
|
||||
let scene, camera, renderer, mobius, clock;
|
||||
let baseRotationY = 0;
|
||||
|
||||
// Setup lil-gui
|
||||
|
||||
// const gui = new GUI();
|
||||
// gui.add(params, 'spinSpeedX', -0.2, 0.2).step(0.001).name('X Spin Speed');
|
||||
// gui.add(params, 'spinSpeedY', -0.2, 0.2).step(0.001).name('Y Spin Speed');
|
||||
// gui.add(params, 'spinSpeedZ', -0.2, 0.2).step(0.001).name('Z Spin Speed');
|
||||
|
||||
|
||||
init();
|
||||
animate();
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
|
||||
const material = new THREE.MeshPhysicalMaterial({
|
||||
color: 0x333333,
|
||||
roughness: 0.24,
|
||||
metalness: 1.0,
|
||||
clearcoat: 0.48,
|
||||
clearcoatRoughness: 0.15,
|
||||
reflectivity: 1.2,
|
||||
sheen: 0.35,
|
||||
sheenColor: new THREE.Color(0x212121),
|
||||
sheenRoughness: 0.168,
|
||||
envMapIntensity: 1,
|
||||
});
|
||||
|
||||
// Add scattered lights
|
||||
addRandomLights();
|
||||
|
||||
// Generate a synthetic cube environment
|
||||
const size = 512;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
|
||||
|
||||
// Gradient: sunset horizon (bottom warm, top cool)
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
||||
gradient.addColorStop(0, '#001133'); // top dark blue
|
||||
gradient.addColorStop(0.4, '#223366'); // mid blue
|
||||
gradient.addColorStop(0.5, '#ff8844'); // orange near horizon
|
||||
gradient.addColorStop(0.51, '#000000'); // black horizon
|
||||
gradient.addColorStop(0.8, '#105010'); // green base
|
||||
gradient.addColorStop(1, '#000000'); // black base
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas);
|
||||
texture.mapping = THREE.EquirectangularReflectionMapping;
|
||||
|
||||
// Apply as environment
|
||||
scene.environment = texture;
|
||||
scene.background = null; // keep transparent
|
||||
|
||||
function expandGradient(img) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const w = 512, h = 256; // safe HDRI-like size
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Stretch the 1px-wide strip to fill the canvas
|
||||
ctx.drawImage(img, 0, 0, w, h);
|
||||
|
||||
return new THREE.CanvasTexture(canvas);
|
||||
}
|
||||
|
||||
const envloader = new THREE.ImageLoader();
|
||||
envloader.load('horizon-gradient.png', (image) => {
|
||||
const tex = expandGradient(image);
|
||||
tex.mapping = THREE.EquirectangularReflectionMapping;
|
||||
|
||||
// ✅ safe to feed into PMREM
|
||||
const pmrem = new THREE.PMREMGenerator(renderer);
|
||||
const envMap = pmrem.fromEquirectangular(tex).texture;
|
||||
|
||||
scene.environment = envMap;
|
||||
});
|
||||
|
||||
|
||||
|
||||
const textDiv = document.createElement('div');
|
||||
textDiv.textContent = "CHORUS";
|
||||
textDiv.style.color = "#ffffff";
|
||||
textDiv.style.fontSize = "96px";
|
||||
textDiv.style.fontFamily = "Exo, sans-serif";
|
||||
textDiv.style.textAlign = "center";
|
||||
|
||||
// Create CSS2DObject and position it at the origin
|
||||
const textLabel = new CSS2DObject(textDiv);
|
||||
textLabel.position.set(0, 0, 0); // exact origin
|
||||
scene.add(textLabel);
|
||||
|
||||
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 100);
|
||||
camera.position.set(0, 0, 1.8);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
const light = new THREE.PointLight(0xffffff, 1.4);
|
||||
light.position.set(0, 4, 1);
|
||||
scene.add(light);
|
||||
|
||||
const bottomLight = new THREE.PointLight(0x800080, 1.2, 12); // (color, intensity, distance)
|
||||
bottomLight.position.set(0, -4, 1); // directly under the model
|
||||
scene.add(bottomLight);
|
||||
|
||||
const leftLight = new THREE.PointLight(0x808000, 1.45, 5); // (color, intensity, distance)
|
||||
leftLight.position.set(-5, 0, 4); // top left of the model
|
||||
scene.add(leftLight);
|
||||
|
||||
scene.add(new THREE.AmbientLight(0xffffff, 0.45));
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
|
||||
const dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderPath( 'https://www.gstatic.com/draco/v1/decoders/' );
|
||||
loader.setDRACOLoader( dracoLoader );
|
||||
loader.load(
|
||||
"./mobius-ring.glb", // ensure mobius.glb is in the same folder as this HTML
|
||||
(gltf) => {
|
||||
mobius = gltf.scene;
|
||||
mobius.traverse((child) => {
|
||||
if (child.isMesh) {
|
||||
child.material = material;
|
||||
}
|
||||
});
|
||||
scene.add(mobius);
|
||||
},
|
||||
undefined,
|
||||
(err) => console.error("Error loading model:", err)
|
||||
);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setClearColor(0x000000, 0); // transparent background
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
clock = new THREE.Clock();
|
||||
window.addEventListener("resize", onWindowResize);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
labelRenderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
if (mobius) {
|
||||
const elapsed = clock.getElapsedTime();
|
||||
|
||||
// Continuous spin
|
||||
mobius.rotation.x += params.spinSpeedX;
|
||||
mobius.rotation.y += params.spinSpeedY;
|
||||
mobius.rotation.z += params.spinSpeedZ;
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
labelRenderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function addRandomLights() {
|
||||
for (let i = 0; i < params.lightCount; i++) {
|
||||
const color = new THREE.Color().setHSL(Math.random(), 0.84, 0.9);
|
||||
const light = new THREE.PointLight(color, params.lightIntensity, params.lightDistance);
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const height = (Math.random() - 0.5) * 4;
|
||||
const radius = 6 + Math.random() * 4;
|
||||
light.position.set(Math.cos(angle) * radius, height, Math.sin(angle) * radius);
|
||||
scene.add(light);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
brand-assets/logos/logo.png
Normal file
|
After Width: | Height: | Size: 126 KiB |