Last active
June 24, 2019 10:50
-
-
Save FransBouma/6959f3b75ebccb9078d716e72b0cb053 to your computer and use it in GitHub Desktop.
Starfield JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html> | |
<head> | |
<title>Simple full-browser star field effect in JS. By Otis / Infuse Project.</title> | |
<style type="text/css"> | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
</style> | |
<script lang="javascript"> | |
// VBL | |
window.requestAnimFrame = (function () { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function (/* function */ callback, /* DOMElement */ element) { | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
window.onresize = function () { | |
fb.resize(); | |
} | |
// constants | |
var MAXZ = 4096; | |
var SCREENZ = 32; | |
var NUMBEROFSTARS = 16384; | |
// globals | |
var fb, counter, stars; | |
function init() { | |
fb = new frameBuffer('fbDiv'); | |
counter = Math.floor(fb.width / 2); | |
stars = new Array(NUMBEROFSTARS); | |
for (i = 0; i < NUMBEROFSTARS; i++) { | |
stars[i] = new star(fb.width*64, fb.height*64); | |
} | |
renderLoop(); | |
} | |
function renderLoop() { | |
requestAnimFrame(renderLoop); | |
renderFrame(); | |
} | |
function renderFrame() { | |
fb.clear(); | |
for (i = 0; i < NUMBEROFSTARS; i++) { | |
stars[i].move(); | |
stars[i].project(fb.width, fb.height); | |
} | |
fb.drawStars(stars); | |
} | |
function frameBuffer(id) { | |
this.canvas = document.createElement('canvas'); | |
this.canvas.style.position = 'absolute'; | |
this.canvas.style.left = this.canvas.style.top = '0px'; | |
this.canvas.width = window.innerWidth; | |
this.canvas.height = window.innerHeight; | |
//var parentDiv = document.getElementById(id); | |
document.body.appendChild(this.canvas); | |
this.width = this.canvas.width; | |
this.height = this.canvas.height; | |
this.ctxt = this.canvas.getContext('2d'); | |
this.clear = function () { | |
//this.ctxt.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
this.fill(0, 0.1); | |
} | |
this.fill = function (color, alpha) { | |
this.ctxt.save(); | |
this.ctxt.globalAlpha = alpha; | |
this.ctxt.fillStyle = color; | |
this.ctxt.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
this.ctxt.restore(); | |
} | |
this.resize = function () { | |
if (this.canvas) { | |
this.canvas.setAttribute('width', window.innerWidth); | |
this.canvas.setAttribute('height', window.innerHeight); | |
this.width = this.canvas.width; | |
this.height = this.canvas.height; | |
this.ctxt = this.canvas.getContext('2d'); | |
} | |
} | |
this.drawStars = function(stars) { | |
var byteBuffer = this.ctxt.getImageData(0, 0, this.canvas.width, this.canvas.height); | |
for (i = 0; i < NUMBEROFSTARS; i++) { | |
var star = stars[i]; | |
if (star.renderStar) { | |
// calculate color alpha from z. the farther away, the less bright (so alpha nears to 0 with MAXZ | |
var color = Math.floor(255 * (1.0 - ((star.z - SCREENZ) / (MAXZ - SCREENZ)))); | |
this.putPixel(byteBuffer, star.projectedX, star.projectedY, color, color, color, 255); | |
} | |
} | |
this.ctxt.putImageData(byteBuffer, 0, 0); | |
} | |
this.putPixel = function(buffer, x, y, r, g, b, a) { | |
var index = (y * this.canvas.width * 4) + (x * 4); | |
buffer.data[index] = r; | |
buffer.data[index + 1] = g; | |
buffer.data[index + 2] = b; | |
buffer.data[index + 3] = a; | |
} | |
} | |
function star(maxWidth, maxHeight) { | |
this.projectedX = 0; | |
this.projectedY = 0; | |
this.renderStar = false; | |
// move origin to center of canvas, so pre-calc x/y according to this. | |
this.x = (Math.random() * maxWidth) - (maxWidth/2); | |
this.y = (Math.random() * maxHeight) - (maxHeight/2); | |
this.z = Math.random() * MAXZ; | |
this.precalcX = Math.floor(this.x * SCREENZ); | |
this.precalcY = Math.floor(this.y * SCREENZ); | |
this.speed = Math.random() * 10; | |
if (this.speed < 1) { | |
this.speed = 1; | |
} | |
this.move = function() { | |
this.z = this.z - this.speed; | |
if (this.z <= 1) { | |
this.z = MAXZ; | |
} | |
} | |
this.project = function(maxWidth, maxHeight) { | |
this.projectedX = Math.floor(( this.precalcX / this.z) + (maxWidth/2)); | |
this.projectedY = Math.floor(( this.precalcY / this.z) + (maxHeight / 2)); | |
this.renderStar = ((this.projectedX >= 0) && (this.projectedX <= maxWidth) && (this.projectedY >= 0) && (this.projectedY <= maxHeight)); | |
} | |
} | |
</script> | |
</head> | |
<body style="background-color:#000;"> | |
<div id="fbDiv"> | |
</div> | |
<script> | |
init(); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html> | |
<head> | |
<title>Simple full-browser star field effect in JS. By Otis / Infuse Project.</title> | |
<style type="text/css"> | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
</style> | |
<script lang="javascript"> | |
// VBL | |
window.requestAnimFrame = (function () { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function (/* function */ callback, /* DOMElement */ element) { | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
window.onresize = function () { | |
fb.resize(); | |
} | |
// constants. X and Y follow normal canvas coordinate system. Z is positive into the screen. | |
var MAXZ = 4096; | |
var SCREENZ = 32; | |
var NUMBEROFSTARS = 16384; | |
// globals | |
var fb, stars, moveDirection, worldMaxX, worldMaxY; | |
function init() { | |
fb = new frameBuffer('fbDiv'); | |
stars = new Array(NUMBEROFSTARS); | |
worldMaxX = fb.width * 64; | |
worldMaxY = fb.height * 64; | |
for (i = 0; i < NUMBEROFSTARS; i++) { | |
stars[i] = new star(); | |
} | |
moveDirection = new moveVector(); | |
renderLoop(); | |
} | |
function renderLoop() { | |
requestAnimFrame(renderLoop); | |
renderFrame(); | |
} | |
function renderFrame() { | |
//fb.fill(0, 0.5); | |
fb.clear(); | |
moveDirection.tick(); | |
for (i = 0; i < NUMBEROFSTARS; i++) { | |
stars[i].move(moveDirection); | |
stars[i].project(fb.width, fb.height); | |
} | |
fb.drawStars(stars); | |
} | |
//////////// | |
// moveVector | |
//////////// | |
function moveVector() { | |
// angles in degrees. | |
this.alpha = 0; | |
this.beta = 90; | |
this.gamma = 275; | |
this.dx = 0; | |
this.dy = 0; | |
this.dz = 0; | |
this.r = 10; | |
this.tick = function () { | |
this.alpha += 0.1; | |
this.beta -= 0.05; | |
this.gamma += 0.1; | |
if(this.alpha > 360) { | |
this.azimuth = 0; | |
} | |
if (this.beta <0) { | |
this.beta = 360; | |
} | |
if (this.gamma > 360) { | |
this.gamma = 0; | |
} | |
this.convert(); | |
} | |
this.convert = function () { | |
// convert angles to radians, then calculate sin/cos for deltas. | |
this.dx = Math.sin(2 * Math.PI * (this.alpha / 360)) * this.r; | |
this.dy = -Math.cos(2 * Math.PI * (this.beta / 360)) * this.r; | |
this.dz = (Math.sin(2 * Math.PI * (this.gamma / 360)) * this.r)/8; | |
} | |
} | |
//////////// | |
// frameBuffer | |
//////////// | |
function frameBuffer(id) { | |
this.canvas = document.createElement('canvas'); | |
this.canvas.style.position = 'absolute'; | |
this.canvas.style.left = this.canvas.style.top = '0px'; | |
this.canvas.width = window.innerWidth; | |
this.canvas.height = window.innerHeight; | |
var parentDiv = document.getElementById(id); | |
parentDiv.appendChild(this.canvas); | |
this.width = this.canvas.width; | |
this.height = this.canvas.height; | |
this.ctxt = this.canvas.getContext('2d'); | |
this.clear = function () { | |
this.ctxt.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
} | |
this.fill = function (color, alpha) { | |
this.ctxt.save(); | |
this.ctxt.globalAlpha = alpha; | |
this.ctxt.fillStyle = color; | |
this.ctxt.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
this.ctxt.restore(); | |
} | |
this.resize = function () { | |
if (this.canvas) { | |
this.canvas.setAttribute('width', window.innerWidth); | |
this.canvas.setAttribute('height', window.innerHeight); | |
this.width = this.canvas.width; | |
this.height = this.canvas.height; | |
this.ctxt = this.canvas.getContext('2d'); | |
} | |
} | |
this.drawStars = function(stars) { | |
var byteBuffer = this.ctxt.getImageData(0, 0, this.canvas.width, this.canvas.height); | |
for (i = 0; i < NUMBEROFSTARS; i++) { | |
var star = stars[i]; | |
if (star.renderStar) { | |
// calculate color alpha from z. the farther away, the less bright (so alpha nears to 0 with MAXZ | |
var color = Math.floor(255 * (1.0 - ((star.z - SCREENZ) / (MAXZ - SCREENZ)))); | |
this.putPixel(byteBuffer, star.projectedX, star.projectedY, color, color, color, 255); | |
} | |
} | |
this.ctxt.putImageData(byteBuffer, 0, 0); | |
} | |
this.putPixel = function(buffer, x, y, r, g, b, a) { | |
var index = (y * this.canvas.width * 4) + (x * 4); | |
buffer.data[index] = r; | |
buffer.data[index + 1] = g; | |
buffer.data[index + 2] = b; | |
buffer.data[index + 3] = a; | |
} | |
} | |
//////////// | |
// star | |
//////////// | |
// World origin is 0,0 which is at top left. Z is positive inside the world. | |
function star() { | |
this.projectedX = 0; | |
this.projectedY = 0; | |
this.renderStar = false; | |
this.x = (Math.random() * worldMaxX); | |
this.y = (Math.random() * worldMaxY); | |
this.z = Math.random() * MAXZ; | |
this.speed = Math.random() * 10; | |
if (this.speed < 1) { | |
this.speed = 1; | |
} | |
this.move = function (v) { | |
this.x += Math.floor(v.dx * this.speed); | |
this.y += Math.floor(v.dy * this.speed); | |
this.z += Math.floor(v.dz * this.speed); | |
// clamp. | |
if(this.x < 0) { | |
this.x = worldMaxX - (0 - this.x); | |
} | |
else { | |
if (this.x > worldMaxX) { | |
this.x -= worldMaxX; | |
} | |
} | |
if (this.y < 0) { | |
this.y = worldMaxY - (0 - this.y); | |
} | |
else { | |
if (this.y > worldMaxY) { | |
this.y -= worldMaxY; | |
} | |
} | |
if (this.z < 1) { | |
this.z = MAXZ; | |
} | |
else { | |
if (this.z > MAXZ) { | |
this.z = 1; | |
} | |
} | |
} | |
// Point of View is in the center of the x, y plane with z = SCREENZ, looking into the world, so translate each x, y of coordinate to center of | |
// world. sMaxWidth is maxWidth of viewport. sMaxHeight is maxHeight of viewport. | |
this.project = function (sMaxWidth, sMaxHeight) { | |
var transX = this.x - (worldMaxX / 2); | |
var transY = this.y - (worldMaxY / 2); | |
this.projectedX = Math.floor((Math.floor(transX * SCREENZ) / this.z) + (sMaxWidth / 2)); | |
this.projectedY = Math.floor((Math.floor(transY * SCREENZ) / this.z) + (sMaxHeight / 2)); | |
this.renderStar = ((this.projectedX >= 0) && (this.projectedX <= sMaxWidth) && (this.projectedY >= 0) && (this.projectedY <= sMaxHeight)); | |
} | |
} | |
</script> | |
</head> | |
<body style="background-color:#000;"> | |
<div id="fbDiv"> | |
</div> | |
<script> | |
init(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment