Skip to main content
Improved in line with congusbongus' comment: Add variation to star movement multiplier, and explain what it does.
Source Link
Anko
  • 13.4k
  • 10
  • 55
  • 82
  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used a random value between 1.00501 and 1.05. This makes stars move away from the middle, faster the further they are from the middle. Greater values of ε cause the star to move faster, creating the illusion that it's closer.
  3. Clear the screen. (Or don't, if you want the hyperspace effect. This makes the streaks of stars stay on screen instead of being cleared.)
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle),
    // The multiplier affects how quickly the star moves.
    // Varying this makes some stars appear closer (faster
    // movement), some further (slower movement).
    multiplier : 1.01 + Math.random() * 0.04
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  // Clear the screen.
  // If you want the hyperspace effect, skip this.
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= 1s.05multiplier
    s.y *= 1s.05multiplier
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>
  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used 1.005. This makes stars move away from the middle, faster the further they are from the middle.
  3. Clear the screen. (Or don't, if you want the hyperspace effect. This makes the streaks of stars stay on screen instead of being cleared.)
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle)
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  // Clear the screen.
  // If you want the hyperspace effect, skip this.
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= 1.05
    s.y *= 1.05
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>
  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used a random value between 1.01 and 1.05. This makes stars move away from the middle, faster the further they are from the middle. Greater values of ε cause the star to move faster, creating the illusion that it's closer.
  3. Clear the screen. (Or don't, if you want the hyperspace effect. This makes the streaks of stars stay on screen instead of being cleared.)
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle),
    // The multiplier affects how quickly the star moves.
    // Varying this makes some stars appear closer (faster
    // movement), some further (slower movement).
    multiplier : 1.01 + Math.random() * 0.04
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  // Clear the screen.
  // If you want the hyperspace effect, skip this.
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= s.multiplier
    s.y *= s.multiplier
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>
Hyperspace!
Source Link
Anko
  • 13.4k
  • 10
  • 55
  • 82
  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used 1.005. This makes stars move away from the middle, faster the further they are from the middle.
  3. Clear the screen. (Or don't, to prepare for renderingif you want the hyperspace effect. This makes the streaks of stars stay on screen instead of being cleared.)
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle)
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  // Clear the screen.
  // If you want the hyperspace effect, skip this.
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= 1.05
    s.y *= 1.05
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>
  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used 1.005. This makes stars move away from the middle, faster the further they are from the middle.
  3. Clear the screen, to prepare for rendering.
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle)
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= 1.05
    s.y *= 1.05
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>
  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used 1.005. This makes stars move away from the middle, faster the further they are from the middle.
  3. Clear the screen. (Or don't, if you want the hyperspace effect. This makes the streaks of stars stay on screen instead of being cleared.)
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle)
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  // Clear the screen.
  // If you want the hyperspace effect, skip this.
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= 1.05
    s.y *= 1.05
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>
Source Link
Anko
  • 13.4k
  • 10
  • 55
  • 82

Every frame—

  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used 1.005. This makes stars move away from the middle, faster the further they are from the middle.
  3. Clear the screen, to prepare for rendering.
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.

Here's a demo in a runnable JavaScript snippet, using HTML Canvas to render:

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle)
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= 1.05
    s.y *= 1.05
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>