Livecoding my father's idea with him, using ES2015 and transitions whose duration is longer than the iteration loop in which the exit transition is a bit tricky with D3 3.*.
Built with blockbuilder.org
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width:100%; height: 100% }
</style>
</head>
<body>
<script>
var svg = d3.select("body").append("svg")
const width = 960
const height = 500
const starNumber = 300
svg.attr({width, height})
svg.style({
'background-color': 'black'
})
const randomStar = () => ({
key: Math.random(),
x: width * Math.random(),
y: height * Math.random(),
existing: true
})
const starData = Array.apply(null, Array(starNumber)).map(randomStar)
window.setInterval(() => {
const stars = svg.selectAll('.star').data(starData, d => d.key)
stars.enter().append('circle').classed('star', true)
stars.attr({
r: 1,
fill: 'white',
cx: d => d.x,
cy: d => d.y,
opacity: 1
})
const timing = Math.random() * 1000 + 250
const maxSize = Math.random() * 100 + 5
stars.exit()
.filter(d => d.existing)
.each(d => {d.existing = false})
.transition().duration(timing).ease('cubic-in')
.attr({
r: maxSize,
opacity: 0.5
})
.transition().duration(timing/4).ease('cubic-out')
.attr({
r: 0,
opacity: 0
})
.remove()
starData.shift()
starData.push(randomStar())
}, 250)
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js