`<!DOCTYPE html>
Three.js Balloon Dog
<br> body { margin: 0; overflow: hidden; background-color: #111827; }<br> canvas { display: block; width: 100vw; height: 100vh; }<br>
<!-- Three.js Library -->
<!-- OrbitControls -->
<script>
// Main initialization function
function init() {
// 1. Scene Setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111827);
scene.fog = new THREE.Fog(0x111827, 20, 100);
// 2. Camera Setup
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(15, 12, 15);
// 3. Renderer Setup
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// 4. Orbit Controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.5;
// 5. Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(10, 20, 10);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
scene.add(dirLight);
// Add some point lights to enhance the metallic reflection
const pointLight1 = new THREE.PointLight(0xffffff, 0.5);
pointLight1.position.set(-10, 10, -10);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight(0xffffff, 0.5);
pointLight2.position.set(10, 5, -10);
scene.add(pointLight2);
// 6. Grid Helper
const gridHelper = new THREE.GridHelper(50, 50, 0x444444, 0x222222);
scene.add(gridHelper);
// 7. Balloon Dog Construction
// Material - Metallic Magenta/Pink style
const balloonMaterial = new THREE.MeshPhysicalMaterial({
color: 0xE0249B, // Magenta
metalness: 0.7,
roughness: 0.15,
clearcoat: 1.0,
clearcoatRoughness: 0.1,
reflectivity: 1.0
});
// Helper to create a balloon segment (Capsule approximation for r128)
function createBalloonSegment(length, radius) {
const group = new THREE.Group();
// Cylinder
const cylinderGeo = new THREE.CylinderGeometry(radius, radius, length, 32);
const cylinder = new THREE.Mesh(cylinderGeo, balloonMaterial);
cylinder.castShadow = true;
cylinder.receiveShadow = true;
group.add(cylinder);
// Top Cap
const sphereGeo = new THREE.SphereGeometry(radius, 32, 16);
const topCap = new THREE.Mesh(sphereGeo, balloonMaterial);
topCap.position.y = length / 2;
topCap.castShadow = true;
topCap.receiveShadow = true;
group.add(topCap);
// Bottom Cap
const bottomCap = new THREE.Mesh(sphereGeo, balloonMaterial);
bottomCap.position.y = -length / 2;
bottomCap.castShadow = true;
bottomCap.receiveShadow = true;
group.add(bottomCap);
return group;
}
// Group to hold the whole dog
const dogGroup = new THREE.Group();
const radius = 0.8;
// --- Body ---
const torsoLength = 4;
const torso = createBalloonSegment(torsoLength, radius);
torso.rotation.z = Math.PI / 2;
torso.position.y = 3.5;
dogGroup.add(torso);
// --- Legs (Back) ---
const legLength = 3.5;
const backLegLeft = createBalloonSegment(legLength, radius);
backLegLeft.position.set(-2, 1.75, 0.6); // Offset X (back), Y (height/2), Z (width)
dogGroup.add(backLegLeft);
const backLegRight = createBalloonSegment(legLength, radius);
backLegRight.position.set(-2, 1.75, -0.6);
dogGroup.add(backLegRight);
// --- Legs (Front) ---
const frontLegLeft = createBalloonSegment(legLength, radius);
frontLegLeft.position.set(2, 1.75, 0.6);
dogGroup.add(frontLegLeft);
const frontLegRight = createBalloonSegment(legLength, radius);
frontLegRight.position.set(2, 1.75, -0.6);
dogGroup.add(frontLegRight);
// --- Neck ---
const neckLength = 2.5;
const neck = createBalloonSegment(neckLength, radius);
neck.position.set(2.2, 5, 0);
neck.rotation.z = -Math.PI / 8; // Slight tilt back
dogGroup.add(neck);
// --- Head ---
// Head is roughly a horizontal segment or a sphere.
// In balloon dogs, the head is a segment with a snout segment attached.
const headLength = 2.0;
const head = createBalloonSegment(headLength, radius);
head.position.set(2.2, 6.8, 0);
head.rotation.x = Math.PI / 2; // Make horizontal relative to camera view? No, rotation logic tricky.
// Let's just place a sphere knot for the main head joint
const headJoint = new THREE.Mesh(new THREE.SphereGeometry(radius * 1.1, 32, 32), balloonMaterial);
headJoint.position.set(2.3, 6.5, 0);
headJoint.castShadow = true;
dogGroup.add(headJoint);
// Snout
const snoutLength = 1.5;
const snout = createBalloonSegment(snoutLength, radius);
snout.rotation.z = Math.PI / 2;
snout.position.set(3.5, 6.5, 0); // Forward from head joint
dogGroup.add(snout);
// Nose tip (small black sphere)
const noseGeo = new THREE.SphereGeometry(0.3, 16, 16);
const noseMat = new THREE.MeshStandardMaterial({ color: 0x000000, roughness: 0.4 });
const nose = new THREE.Mesh(noseGeo, noseMat);
nose.position.set(4.4, 6.5, 0);
dogGroup.add(nose);
// --- Ears ---
const earLength = 3.5;
const earLeft = createBalloonSegment(earLength, radius);
earLeft.position.set(2.3, 8.5, 0.5);
dogGroup.add(earLeft);
const earRight = createBalloonSegment(earLength, radius);
earRight.position.set(2.3, 8.5, -0.5);
dogGroup.add(earRight);
// --- Tail ---
const tailLength = 1.5;
const tail = createBalloonSegment(tailLength, radius * 0.8);
tail.position.set(-2.2, 4.5, 0);
tail.rotation.z = Math.PI / 4;
dogGroup.add(tail);
// Add dog to scene
scene.add(dogGroup);
// 8. Animation Loop
function animate() {
requestAnimationFrame(animate);
controls.update(); // Required for autoRotate and damping
renderer.render(scene, camera);
}
animate();
// 9. Resize Handler
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 10. Parent Interactivity Functions
// Defined inside init() to access closure variables (controls, gridHelper)
window.resetCamera = function() {
controls.reset();
// If reset() stops autoRotate in some versions, ensure it's re-enabled
controls.autoRotate = true;
camera.position.set(15, 12, 15);
camera.lookAt(0, 0, 0);
};
window.toggleGrid = function() {
gridHelper.visible = !gridHelper.visible;
};
}
// Start everything
init();
</script>

Top comments (0)