Models 44

Dots3-Note Preview (free)

by Dots StudioReasoningClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
33.4 s
Cost
$0
Output tokens
3,313
Spent thinking
0 tokens
Speed
102.3 tok/s
Console errors
0
First token after
1.0 s
First visible text after
1.0 s
Prompt tokens
566
Code size
12.2 KB
Finish reason
stop
Served by
AtlasCloud
Model id
dots-studio/dots-3-note-preview:free
Reasoning sent
{"effort":"none"}
Max output
32,000 tokens
Generated
Nov 9, 2026

What it was asked

The prompt
Build a low-poly 3D scene of a campfire in a forest clearing at night, with Three.js in a single HTML file. Load Three.js only through this import map (do not use any other URL or version): <script type="importmap">{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js","three/addons/":"https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"}}</script> Requirements: - A ground with a few low-poly rocks and logs around a campfire in the center. - At least 30 low-poly trees placed randomly around the clearing (not on the fire). - The campfire has animated flames and a warm point light that flickers and casts shadows. - Sparks rising from the fire as a simple particle system. - A dark night sky with stars and a moon, plus fog that fades the distant trees. - OrbitControls to look around, with limits so the camera stays above the ground. - The renderer fills the window and handles resizing correctly.
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task.

Output rules:
- Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block.
- Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task.
- Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw.
- The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar.
- Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction.
- Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session.
- Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing.
- The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message.
- The code must run with zero errors in the console of the latest Chrome.

Code run-1.html · 337 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Low-Poly Campfire Scene</title>
    <style>
        body { margin: 0; overflow: hidden; background: #000; }
        canvas { display: block; }
        #start-msg {
            position: absolute;
            top: 50%; left: 50%;
            transform: translate(-50%, -50%);
            color: #fff;
            font-family: sans-serif;
            font-size: 1.2rem;
            background: rgba(0,0,0,0.6);
            padding: 1rem 2rem;
            border-radius: 8px;
            cursor: pointer;
            z-index: 10;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="start-msg">Click or press a key to start</div>
    <script type="importmap">
    {
        "imports": {
            "three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
            "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
        }
    }
    </script>

    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

        // --- Scene Setup ---
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x0a0a1a);
        scene.fog = new THREE.FogExp2(0x0a0a1a, 0.015);

        // --- Camera ---
        const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 500);
        camera.position.set(0, 8, 12);

        // --- Renderer ---
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        document.body.appendChild(renderer.domElement);

        // --- Controls ---
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.minDistance = 6;
        controls.maxDistance = 40;
        controls.maxPolarAngle = Math.PI * 0.45; // prevent going below ground
        controls.target.set(0, 1, 0);

        // --- Lighting ---
        // Ambient
        const ambient = new THREE.AmbientLight(0x222233, 0.4);
        scene.add(ambient);

        // Moonlight (directional)
        const moon = new THREE.DirectionalLight(0x4455aa, 0.6);
        moon.position.set(10, 20, 5);
        moon.castShadow = true;
        moon.shadow.mapSize.set(1024, 1024);
        moon.shadow.camera.near = 0.5;
        moon.shadow.camera.far = 50;
        moon.shadow.camera.left = -15;
        moon.shadow.camera.right = 15;
        moon.shadow.camera.top = 15;
        moon.shadow.camera.bottom = -15;
        scene.add(moon);

        // Campfire point light (flickering)
        const fireLight = new THREE.PointLight(0xff6622, 2.5, 0, 1.5);
        fireLight.position.set(0, 1.5, 0);
        fireLight.castShadow = true;
        fireLight.shadow.mapSize.set(512, 512);
        fireLight.shadow.camera.near = 0.1;
        fireLight.shadow.camera.far = 10;
        scene.add(fireLight);

        // --- Ground ---
        const groundGeo = new THREE.PlaneGeometry(100, 100);
        const groundMat = new THREE.MeshStandardMaterial({ color: 0x2a3a1a, roughness: 0.9 });
        const ground = new THREE.Mesh(groundGeo, groundMat);
        ground.rotation.x = -Math.PI / 2;
        ground.receiveShadow = true;
        scene.add(ground);

        // --- Sky: Stars + Moon ---
        function createSky() {
            // Star field
            const starGeo = new THREE.BufferGeometry();
            const starCount = 800;
            const positions = new Float32Array(starCount * 3);
            for (let i = 0; i < starCount; i++) {
                positions[i * 3] = (Math.random() - 0.5) * 200;
                positions[i * 3 + 1] = Math.random() * 60 + 20;
                positions[i * 3 + 2] = (Math.random() - 0.5) * 200;
            }
            starGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
            const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.3, sizeAttenuation: true });
            const stars = new THREE.Points(starGeo, starMat);
            scene.add(stars);

            // Moon
            const moonGeo = new THREE.SphereGeometry(1.5, 16, 16);
            const moonMat = new THREE.MeshBasicMaterial({ color: 0xffffcc });
            const moonMesh = new THREE.Mesh(moonGeo, moonMat);
            moonMesh.position.set(20, 30, -10);
            scene.add(moonMesh);
        }
        createSky();

        // --- Campfire Center ---
        const fireGroup = new THREE.Group();
        scene.add(fireGroup);

        // Fire base (stones)
        const stoneMat = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.9 });
        for (let i = 0; i < 8; i++) {
            const angle = (i / 8) * Math.PI * 2;
            const radius = 0.8;
            const x = Math.cos(angle) * radius;
            const z = Math.sin(angle) * radius;
            const stone = new THREE.Mesh(
                new THREE.DodecahedronGeometry(0.25, 0),
                stoneMat
            );
            stone.position.set(x, 0.2, z);
            stone.castShadow = true;
            stone.receiveShadow = true;
            fireGroup.add(stone);
        }

        // Logs
        const logMat = new THREE.MeshStandardMaterial({ color: 0x5a3a1a, roughness: 0.8 });
        for (let i = 0; i < 4; i++) {
            const angle = (i / 4) * Math.PI * 2;
            const log = new THREE.Mesh(
                new THREE.CylinderGeometry(0.1, 0.15, 1.2, 6),
                logMat
            );
            log.position.set(Math.cos(angle) * 0.5, 0.3, Math.sin(angle) * 0.5);
            log.rotation.z = Math.PI / 2;
            log.rotation.y = angle;
            log.castShadow = true;
            log.receiveShadow = true;
            fireGroup.add(log);
        }

        // --- Animated Flames ---
        const flameCount = 12;
        const flames = [];
        const flameMat = new THREE.MeshBasicMaterial({ color: 0xff5522, transparent: true, opacity: 0.8, side: THREE.DoubleSide });

        for (let i = 0; i < flameCount; i++) {
            const flame = new THREE.Mesh(
                new THREE.PlaneGeometry(0.3, 0.6),
                flameMat
            );
            flame.position.set(
                (Math.random() - 0.5) * 0.4,
                0.5 + Math.random() * 0.3,
                (Math.random() - 0.5) * 0.4
            );
            flame.rotation.y = Math.random() * Math.PI;
            flame.userData.phase = Math.random() * Math.PI * 2;
            flames.push(flame);
            fireGroup.add(flame);
        }

        // --- Sparks Particle System ---
        const sparkCount = 60;
        const sparkGeo = new THREE.BufferGeometry();
        const sparkPositions = new Float32Array(sparkCount * 3);
        const sparkVelocities = new Float32Array(sparkCount * 3);
        const sparkLifetimes = new Float32Array(sparkCount);

        for (let i = 0; i < sparkCount; i++) {
            sparkPositions[i * 3] = (Math.random() - 0.5) * 0.5;
            sparkPositions[i * 3 + 1] = 0.5;
            sparkPositions[i * 3 + 2] = (Math.random() - 0.5) * 0.5;

            sparkVelocities[i * 3] = (Math.random() - 0.5) * 0.02;
            sparkVelocities[i * 3 + 1] = 0.02 + Math.random() * 0.03;
            sparkVelocities[i * 3 + 2] = (Math.random() - 0.5) * 0.02;

            sparkLifetimes[i] = Math.random() * 2.0;
        }

        sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
        const sparkMat = new THREE.PointsMaterial({
            color: 0xffaa33,
            size: 0.08,
            transparent: true,
            opacity: 0.9,
            depthWrite: false
        });
        const sparks = new THREE.Points(sparkGeo, sparkMat);
        fireGroup.add(sparks);

        // --- Trees (Low-Poly) ---
        const treeCount = 30;
        const trees = [];
        const treeMat = new THREE.MeshStandardMaterial({ color: 0x2a4a1a, roughness: 0.8 });
        const trunkMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.9 });

        function createTree(x, z) {
            const tree = new THREE.Group();

            // Trunk
            const trunk = new THREE.Mesh(
                new THREE.CylinderGeometry(0.15, 0.2, 1.5, 5),
                trunkMat
            );
            trunk.position.y = 0.75;
            trunk.castShadow = true;
            trunk.receiveShadow = true;
            tree.add(trunk);

            // Foliage (low-poly cone)
            const foliage = new THREE.Mesh(
                new THREE.ConeGeometry(0.8, 1.8, 5),
                treeMat
            );
            foliage.position.y = 2.0;
            foliage.castShadow = true;
            foliage.receiveShadow = true;
            tree.add(foliage);

            tree.position.set(x, 0, z);
            return tree;
        }

        // Place trees avoiding the fire area
        for (let i = 0; i < treeCount; i++) {
            let x, z, dist;
            do {
                x = (Math.random() - 0.5) * 60;
                z = (Math.random() - 0.5) * 60;
                dist = Math.sqrt(x * x + z * z);
            } while (dist < 5 || dist > 30); // Avoid fire and too far

            const tree = createTree(x, z);
            scene.add(tree);
            trees.push(tree);
        }

        // --- Rocks ---
        const rockMat = new THREE.MeshStandardMaterial({ color: 0x555555, roughness: 0.95 });
        for (let i = 0; i < 10; i++) {
            const angle = Math.random() * Math.PI * 2;
            const radius = 3 + Math.random() * 5;
            const x = Math.cos(angle) * radius;
            const z = Math.sin(angle) * radius;
            const rock = new THREE.Mesh(
                new THREE.DodecahedronGeometry(0.3 + Math.random() * 0.4, 0),
                rockMat
            );
            rock.position.set(x, 0.3, z);
            rock.rotation.y = Math.random() * Math.PI;
            rock.castShadow = true;
            rock.receiveShadow = true;
            scene.add(rock);
        }

        // --- Animation Loop ---
        const clock = new THREE.Clock();

        function animate() {
            requestAnimationFrame(animate);
            const delta = clock.getDelta();
            const time = clock.elapsedTime;

            // Animate flames
            flames.forEach((flame, i) => {
                flame.scale.y = 0.8 + Math.sin(time * 10 + flame.userData.phase) * 0.3;
                flame.material.opacity = 0.6 + Math.sin(time * 15 + flame.userData.phase) * 0.2;
            });

            // Flicker fire light
            fireLight.intensity = 2.0 + Math.sin(time * 20) * 0.5 + Math.random() * 0.3;

            // Update sparks
            const positions = sparkGeo.attributes.position.array;
            for (let i = 0; i < sparkCount; i++) {
                positions[i * 3] += sparkVelocities[i * 3];
                positions[i * 3 + 1] += sparkVelocities[i * 3 + 1];
                positions[i * 3 + 2] += sparkVelocities[i * 3 + 2];

                sparkLifetimes[i] -= delta;
                if (sparkLifetimes[i] <= 0 || positions[i * 3 + 1] > 4) {
                    // Reset spark
                    positions[i * 3] = (Math.random() - 0.5) * 0.5;
                    positions[i * 3 + 1] = 0.5;
                    positions[i * 3 + 2] = (Math.random() - 0.5) * 0.5;
                    sparkLifetimes[i] = 2.0;
                }
            }
            sparkGeo.attributes.position.needsUpdate = true;

            controls.update();
            renderer.render(scene, camera);
        }

        // --- Resize Handler ---
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        // --- Start on User Interaction ---
        let started = false;
        function start() {
            if (started) return;
            started = true;
            document.getElementById('start-msg').style.display = 'none';
            animate();
        }

        window.addEventListener('click', start);
        window.addEventListener('keydown', start);
    </script>
</body>
</html>