Full Code of jes/nightdrive for AI

master 4a8899372bf6 cached
6 files
15.1 KB
4.7k tokens
13 symbols
1 requests
Download .txt
Repository: jes/nightdrive
Branch: master
Commit: 4a8899372bf6
Files: 6
Total size: 15.1 KB

Directory structure:
gitextract_olwi0mbm/

├── index.html
└── js/
    ├── car.js
    ├── nightdrive.js
    ├── plane.js
    ├── scene.js
    └── vector.js

================================================
FILE CONTENTS
================================================

================================================
FILE: index.html
================================================
<!doctype html>
<html>
<head>
<title>Nightdrive</title>
<style type="text/css">
html,body {
    width: 100vw;
    height: 100vh;
    margin: 0;
    padding: 0;
    cursor: none;
}
</style>
</head>
<body>
<span style="font-size:2em; position:absolute; color: #888; font-family: monospace" id="clickformusic">Click to toggle music</span>
<canvas id="canvas" style="display:block; width:100vw; height:100vh"></canvas>
<!-- music from https://www.youtube.com/watch?v=4s%2d%2dbtQafWs -->
<audio id="audio" src="music.mp3" loop></audio>
<script src="js/vector.js?v3"></script>
<script src="js/scene.js?v3"></script>
<script src="js/car.js?v3"></script>
<script src="js/plane.js?v3"></script>
<script src="js/nightdrive.js?v3"></script>
</body>
</html>


================================================
FILE: js/car.js
================================================
function Car() {
    // physics
    this.pos = new V2d(0,1);
    this.vel = new V2d(0,0.005);

    // lights
    this.braking = false;
    this.mainbeam = false;
    this.leftindicator = false;
    this.rightindicator = false;
    this.indicatorperiod = 0.9 + Math.random()*0.2; // seconds
    this.indication = 0;
    this.lanes = [0]; // x coordinates of lanes
    this.lane = 0;

    // car configuration
    const lightheight = Math.random()*0.2+0.4;
    const lightwidth = Math.random()*0.3+1.3;
    const lightradius = Math.random()*0.05+0.1;

    const rearlightcolour = colour(180,255, 0,50, 0,50);
    this.rearlights = [
        {xy: new V2d(-lightwidth/2, 0.0), z: lightheight, r: lightradius, col: rearlightcolour},
        {xy: new V2d( lightwidth/2, 0.0), z: lightheight, r: lightradius, col: rearlightcolour},
    ];

    const headlightcolour = colour(180,255, 180,255, 180,255);
    this.headlights = [
        {xy: new V2d(-lightwidth/2, 0.0), z: lightheight, r: lightradius, col: headlightcolour},
        {xy: new V2d( lightwidth/2, 0.0), z: lightheight, r: lightradius, col: headlightcolour},
    ];

    const indicatorcolour = colour(180,255, 100,200, 0,100);
    const indicatorwidth = lightwidth + 0.2 + Math.random()*0.1;
    const indicatorheight = lightheight - 0.1 + Math.random()*0.2;
    const indicatorradius = lightradius - (0.04 + Math.random()*0.03);
    this.leftindicatorlights = [
        {xy: new V2d(-indicatorwidth/2, 0.0), z: indicatorheight, r: indicatorradius, col: indicatorcolour},
    ];
    this.rightindicatorlights = [
        {xy: new V2d( indicatorwidth/2, 0.0), z: indicatorheight, r: indicatorradius, col: indicatorcolour},
    ];
};

Car.prototype.render = function(scene) {
    if (this.vel.y > 0) {
        // moving in same direction as viewer: draw rearlights
        this.drawLights(scene, this.rearlights);
    } else {
        // moving in opposite direction to viewer: draw headlights
        this.drawLights(scene, this.headlights);
    }

    if (this.leftindicator && this.indication > (this.indicatorperiod/2)) this.drawLights(scene, this.leftindicatorlights);
    if (this.rightindicator && this.indication > (this.indicatorperiod/2)) this.drawLights(scene, this.rightindicatorlights);
};

Car.prototype.drawLights = function(scene, lights) {
    for (l of lights) {
        scene.drawCircle(this.pos.add(l.xy), l.z, l.r, l.col);
    }
};

Car.prototype.step = function(dt) {
    this.pos = this.pos.add(this.vel.mul(dt));

    this.indication += dt;
    while (this.indication > this.indicatorperiod)
        this.indication -= this.indicatorperiod;

    const wrapy = 2500;
    while (this.pos.y > observer.pos.y+wrapy && this.vel.y > observer.vel.y)
        this.pos.y -= wrapy;
    while (this.pos.y < observer.pos.y && this.vel.y < observer.vel.y)
        this.pos.y += wrapy;

    // which lane are we in?
    let mylane = 0;
    for (let i = 0; i < this.lanes.length; i++) {
        if (Math.abs(this.pos.x-this.lanes[i]) < (Math.abs(this.pos.x-this.lanes[mylane]))) mylane = i;
    }
    this.lane = mylane;

    const k = Math.sign(this.vel.y);

    if (this.changelane) {
        const m = this.targetlane - this.sourcelane;

        if (this.lane == this.targetlane) {
            // decelerate in x
            this.vel = new V2d(this.vel.x-k*m*0.79*dt, this.vel.y);
        } else {
            // accelerate in x
            this.vel = new V2d(this.vel.x+k*m*0.8*dt, this.vel.y);
        }

        // once we reach the centre of the target lane, snap to centre and stop changing lane
        if (Math.sign(this.pos.x-this.lanes[this.targetlane]) != Math.sign(this.pos.x+this.vel.x*4*dt - this.lanes[this.targetlane])) {
            this.lane = this.targetlane;
            this.pos = new V2d(this.lanes[this.lane], this.pos.y);
            this.vel = new V2d(0, this.vel.y);
            this.changelane = false;
        }
    }

    const collision_secs = 7.0;

    const min_clearance = 2.0; // metres

    let leftlanesafe = this.lane > 0;

    loop:
    //for (car of cars) {
    for (let j = 0; j < cars.length; j++) {
        const car = cars[j];
        if (car == this) continue;
        if (Math.sign(car.vel.y*this.vel.y) == -1) continue;

        for (let i = -1; i <= 1; i++) {
            const yoff = i*wrapy;

            // if we're not in the fast lane, and we're behind this car, and it's in our lane, and we're going faster, and we'll hit it within N seconds, change lanes
            if (Math.sign(car.vel.y)==k && this.lane < this.lanes.length-1 && this.lane<=car.lane && k*(this.pos.y+yoff) < k*car.pos.y && k*this.vel.y > k*car.vel.y && k*(this.pos.y+yoff+this.vel.y*collision_secs+min_clearance) > k*(car.pos.y+car.vel.y*collision_secs)) {
                this.changelane = true;
                this.sourcelane = this.lane;
                this.targetlane = this.lane+1;
                break loop;
            }

            // the left lane is not safe if there is a car in it that we'd hit within 3N seconds
            if (Math.sign(car.vel.y)==k && car.lane==this.lane-1 && k*(this.pos.y+yoff) < k*car.pos.y && k*this.vel.y > k*car.vel.y && k*(this.pos.y+yoff+this.vel.y*collision_secs*3) > k*(car.pos.y+car.vel.y*collision_secs*3+min_clearance)) {
                leftlanesafe = false;
            }
        }
    }

    // move left if we're not changing lane and the left lane is safe
    if (!this.changelane && leftlanesafe) {
        this.changelane = true;
        this.sourcelane = this.lane;
        this.targetlane = this.lane-1;
    }

    if (this.changelane) {
        this.rightindicator = this.targetlane > this.sourcelane;
        this.leftindicator = this.targetlane < this.sourcelane;
    } else {
        this.indication = 0;
        this.leftindicator = false;
        this.rightindicator = false;
    }
};

function colour(r1,r2, g1,g2, b1,b2) {
    const r = r1 + Math.random() * (r2-r1);
    const g = g1 + Math.random() * (g2-g1);
    const b = b1 + Math.random() * (b2-b1);
    return `rgb(${r},${g},${b})`;
}


================================================
FILE: js/nightdrive.js
================================================
let laststep = null;
let observer;
let cars = [];
let planes = [];

let lastwidth;
let lastheight;

const lanes = [-10,-7,-4];
const speed = [60,66,80]; // mph
const catseyedist = 20; // metres
const streetlightdist = 107; // metres
const started = Date.now();
const musiclabeltime = 5000; // ms

function init() {
    observer = new Car();
    observer.pos.x = lanes[1];
    observer.vel.y = speed[1] * 1600/3600;
    observer.lanes = lanes;

    const ourlanes = lanes;
    const theirlanes = ourlanes.map((x) => -x);

    for (let i = 0; i < 100; i++) {
        const car = new Car();
        const lane = Math.floor(Math.random()*lanes.length);
        car.pos = new V2d(lanes[lane],i*25);
        const mph = speed[lane];
        car.vel = new V2d(0, mph * 1600 / 3600);
        car.lanes = ourlanes;
        if (Math.random() < 0.5) {
            car.vel.y = -car.vel.y;
            car.pos.x = -car.pos.x;
            car.lanes = theirlanes;
        }
        car.vel.y += Math.random() * 20 - 10;
        cars.push(car);
    }

    cars.push(observer);

    const canvas = document.getElementById('canvas');
    resize(canvas);

    render();
}

function render() {
    step();

    const canvas = document.getElementById('canvas');
    if (canvas.clientWidth != lastwidth || canvas.clientHeight != lastheight) {
        resize(canvas);
    }
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'rgba(1,1,1,0.3)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    const scene = new Scene(ctx);
    scene.viewpoint = observer.pos;
    scene.viewz = 1.0; // 1 metre off ground

    catseyes(scene, lanes[0]-1.5, '#811');
    catseyes(scene, lanes[0]+1.5, '#666');
    catseyes(scene, lanes[1]+1.5, '#666');
    catseyes(scene, lanes[2]+1.5, '#861');
    streetlights(scene, lanes[0]-3.5);
    streetlights(scene, -0.5);
    streetlights(scene, -(lanes[0]-3.5));
    streetlights(scene, 0.5);

    for (car of cars) {
        car.render(scene);
    }
    for (plane of planes) {
        plane.render(scene);
    }

    const label = document.getElementById('clickformusic');
    label.style.top = `${canvas.height/2 - 100}px`;
    label.style.left = `${canvas.width/2 - label.clientWidth/2}px`;

    const time = Date.now() - started;
    if (time < musiclabeltime) {
        const col = (musiclabeltime-time) * (200/musiclabeltime);
        label.style.color = `rgb(${col}, ${col}, ${col})`;
    } else {
        label.style.display = 'none';
    }

    scene.render();

    window.requestAnimationFrame(render);
}

function catseyes(scene, x, col) {
    const numlines = Math.floor(observer.pos.y / catseyedist);
    const starty = (numlines-1)*catseyedist;
    for (let y = starty; y < observer.pos.y+100; y += catseyedist) {
        scene.drawCircle(new V2d(x-0.02, y), 0.01, 0.015, col, {no_occlude: true});
        scene.drawCircle(new V2d(x+0.02, y), 0.01, 0.015, col, {no_occlude: true});
    }
}

function streetlights(scene, x) {
    const numlines = Math.floor(observer.pos.y / streetlightdist);
    const starty = (numlines-1)*streetlightdist;
    for (let y = starty; y < observer.pos.y+3000; y += streetlightdist) {
        const col = `rgb(255,255,${150+((99*x)+(31*y))%101})`;
        scene.drawCircle(new V2d(x, y), 5, 0.2, col);
    }
}

function step() {
    const now = Date.now();
    if (!laststep) {
        laststep = now;
        return;
    }

    const dt = (now - laststep) / 1000;
    laststep = now;

    for (car of cars) {
        car.step(dt);
    }
    for (plane of planes) {
        plane.step(dt);
    }
    // delete planes that are behind the viewer
    planes = planes.filter((p) => p.pos.y > observer.pos.y);
    // add new planes occasionally
    if (Math.random() < 0.00005 && planes.length < 4) {
        let plane = new Plane();
        plane.pos = new V2d(observer.pos.x-10000-Math.random()*2000, observer.pos.y+20000+Math.random()*5000);
        plane.vel = new V2d(30+Math.random()*20, Math.random()*10-5);
        if (Math.random() < 0.5) {
            plane.pos = new V2d(-plane.pos.x, plane.pos.y);
            plane.vel.x = -plane.vel.x;
        }
        plane.z = 3000+Math.random()*2000;
        planes.push(plane);
    }
}

function resize(canvas) {
    lastwidth = canvas.width = canvas.clientWidth;
    lastheight = canvas.height = canvas.clientHeight;

    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'rgb(0,0,0)';
    ctx.beginPath();
    ctx.rect(0, 0, canvas.width, canvas.height);
    ctx.fill();
}

init();

let playing = false;
document.getElementById('canvas').onclick = function() {
    if (playing) document.getElementById('audio').pause();
    else document.getElementById('audio').play();
    playing = !playing;
}


================================================
FILE: js/plane.js
================================================
function Plane() {
    // physics
    this.pos = new V2d(0,0);
    this.vel = new V2d(25,15);
    this.z = 3000;
    this.t = 0;
    this.period = 1.2+Math.random()*0.2;
};

Plane.prototype.render = function(scene) {
    // red when moving left, green when moving right
    const col = this.vel.x > 0 ? '#585' : '#855';

    if (this.t > this.period/2)
        scene.drawCircle(this.pos, this.z, 12, col);
};

Plane.prototype.step = function(dt) {
    this.pos = this.pos.add(this.vel.mul(dt));

    this.t += dt;
    while (this.t > this.period)
        this.t -= this.period;
};


================================================
FILE: js/scene.js
================================================
const fullcircle = 180*Math.PI;

function Scene(ctx) {
    this.ctx = ctx;
    this.viewpoint = new V2d(0,0);
    this.viewz = 1.0;
    this.viewscale = 1200;
    this.distscale = 2;
    this.circles = [];
}

Scene.prototype.drawCircle = function(pos, z, r, col, opts) {
    let circle = this.project(pos, z, r);
    if (!circle) return;
    circle.col = col;
    circle.roady = pos.y;

    let ground = this.project(pos, 0, 0);
    circle.yground = ground.y;

    if (opts && opts.no_occlude) circle.no_occlude = true;

    this.circles.push(circle);
};

Scene.prototype.render = function() {
    this.ctx.globalCompositeOperation = 'lighter';
    this.ctx.globalAlpha = 0.2;
    // get the nearest circles first
    this.circles.sort((a,b) => {
        return a.roady - b.roady;
    });

    // work out which circles are occluded by the road
    let highestroad = this.ctx.canvas.height;
    for (circle of this.circles) {
        if (circle.yground < highestroad) highestroad = circle.yground;
        if (circle.y > highestroad && !circle.no_occlude) circle.occluded = true;
    }

    for (circle of this.circles) {
        if (circle.occluded) continue;

        this.ctx.fillStyle = circle.col;

        for (let k = 1.0; k > 0; k -= 0.15) {
            this.ctx.beginPath();
            this.ctx.arc(circle.x, circle.y, circle.r*1.5*k*k, 0, fullcircle);
            this.ctx.fill();
        }
    }
    this.ctx.globalCompositeOperation = 'source-over';
    this.ctx.globalAlpha = 1.0;
};

Scene.prototype.project = function(pos, z, r) {
    const dy = 0.1;
    const dx = bend(this.viewpoint.y+dy) - bend(this.viewpoint.y);
    const theta = Math.atan2(dx,dy);
    const posrel1 = pos.add(new V2d(bend(pos.y),0)).sub(this.viewpoint.add(new V2d(bend(this.viewpoint.y),0)));
    const posrel = posrel1.rotate(theta);

    z = z + terrain(pos.y);

    const zrel = z - (this.viewz + terrain(this.viewpoint.y));

    // things behind the viewer are not visible
    if (posrel.y <= 0) return null;

    const dist = this.distscale * Math.sqrt(posrel.y*posrel.y + zrel*zrel);

    // things too close are not visible
    if (dist < 0.5) return null;

    const scaleratio = this.viewscale * this.ctx.canvas.width / 640;

    const screenx = (this.ctx.canvas.width/2) + scaleratio * (posrel.x / dist);
    const screeny = (this.ctx.canvas.height/2) - scaleratio * (zrel / dist);
    const screenr = scaleratio * (r / dist); // px

    return {
        x: screenx,
        y: screeny,
        r: screenr,
    };
};

function terrain(y) {
    return 10*Math.sin(y/1000) + 5*Math.cos(y/527) + 2*Math.sin(y/219);
}

function bend(y) {
    return 200*Math.sin(y/909) + 51*Math.cos(y/517) + 23*Math.sin(y/201);
}


================================================
FILE: js/vector.js
================================================
function V2d(x,y) {
    this.x = x;
    this.y = y;
}

V2d.prototype.add = function(v) {
    return new V2d(this.x + v.x, this.y + v.y);
};

V2d.prototype.sub = function(v) {
    return new V2d(this.x - v.x, this.y - v.y);
};

V2d.prototype.mul = function(k) {
    return new V2d(this.x * k, this.y * k);
};

V2d.prototype.length = function() {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

V2d.prototype.angle = function() {
    return Math.atan2(this.x, this.y);
};

V2d.prototype.rotate = function(theta) {
    return new V2d(Math.cos(theta)*this.x - Math.sin(theta)*this.y, Math.sin(theta)*this.x + Math.cos(theta)*this.y);
};
Download .txt
gitextract_olwi0mbm/

├── index.html
└── js/
    ├── car.js
    ├── nightdrive.js
    ├── plane.js
    ├── scene.js
    └── vector.js
Download .txt
SYMBOL INDEX (13 symbols across 5 files)

FILE: js/car.js
  function Car (line 1) | function Car() {
  function colour (line 154) | function colour(r1,r2, g1,g2, b1,b2) {

FILE: js/nightdrive.js
  function init (line 16) | function init() {
  function render (line 49) | function render() {
  function catseyes (line 97) | function catseyes(scene, x, col) {
  function streetlights (line 106) | function streetlights(scene, x) {
  function step (line 115) | function step() {
  function resize (line 147) | function resize(canvas) {

FILE: js/plane.js
  function Plane (line 1) | function Plane() {

FILE: js/scene.js
  function Scene (line 3) | function Scene(ctx) {
  function terrain (line 88) | function terrain(y) {
  function bend (line 92) | function bend(y) {

FILE: js/vector.js
  function V2d (line 1) | function V2d(x,y) {
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
  {
    "path": "index.html",
    "chars": 746,
    "preview": "<!doctype html>\n<html>\n<head>\n<title>Nightdrive</title>\n<style type=\"text/css\">\nhtml,body {\n    width: 100vw;\n    height"
  },
  {
    "path": "js/car.js",
    "chars": 6047,
    "preview": "function Car() {\n    // physics\n    this.pos = new V2d(0,1);\n    this.vel = new V2d(0,0.005);\n\n    // lights\n    this.br"
  },
  {
    "path": "js/nightdrive.js",
    "chars": 4720,
    "preview": "let laststep = null;\nlet observer;\nlet cars = [];\nlet planes = [];\n\nlet lastwidth;\nlet lastheight;\n\nconst lanes = [-10,-"
  },
  {
    "path": "js/plane.js",
    "chars": 581,
    "preview": "function Plane() {\n    // physics\n    this.pos = new V2d(0,0);\n    this.vel = new V2d(25,15);\n    this.z = 3000;\n    thi"
  },
  {
    "path": "js/scene.js",
    "chars": 2710,
    "preview": "const fullcircle = 180*Math.PI;\n\nfunction Scene(ctx) {\n    this.ctx = ctx;\n    this.viewpoint = new V2d(0,0);\n    this.v"
  },
  {
    "path": "js/vector.js",
    "chars": 642,
    "preview": "function V2d(x,y) {\n    this.x = x;\n    this.y = y;\n}\n\nV2d.prototype.add = function(v) {\n    return new V2d(this.x + v.x"
  }
]

About this extraction

This page contains the full source code of the jes/nightdrive GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (15.1 KB), approximately 4.7k tokens, and a symbol index with 13 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!