summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/_posts/2021-05-16-new-years-resolution-vibe-check.md62
-rw-r--r--src/_posts/2021-05-26-viz-4.md213
2 files changed, 275 insertions, 0 deletions
diff --git a/src/_posts/2021-05-16-new-years-resolution-vibe-check.md b/src/_posts/2021-05-16-new-years-resolution-vibe-check.md
new file mode 100644
index 0000000..965eac8
--- /dev/null
+++ b/src/_posts/2021-05-16-new-years-resolution-vibe-check.md
@@ -0,0 +1,62 @@
+---
+title: >-
+ New Year's Resolution Vibe Check
+description: >-
+ The not-quite-halfway progress report.
+---
+
+It's been over five months since I started my New Year's resolution, where I
+committed to writing 52 blog posts by the end of the year. This week I'm on the
+first vacation I've been able to take since the pandemic started, and, for lack
+of anything else to really write about, am doing an almost-halfway checkup on
+the whole process.
+
+Almost immediately into the process I wished I'd set my sights a bit lower. One
+post a week is a pretty intense pace, it turns out. If I were to reset the
+parameters of the resolution I would probably halve the requirements, down to
+26 posts in the year. One concern would be that I would be more likely to forget
+to do the bi-weekly post, whereas with the current system it's coupled with my
+normal work rhythm and so stays more top of mind. But I think I'd have a much
+easier time (perhaps even twice as easy!), so it might balance out.
+
+My thought in the beginning was that I could write on Friday afternoons or
+Monday mornings as a bookend to working, but what's generally happened is that I
+write on weekends. During the week the energy to write something up just isn't
+there; writing posts is a kind of work all on its own, and I can only bring
+myself to do so much work everyday.
+
+Lately it's been particularly difficult to pump out the posts. Obviously a large
+component of this is that I quickly picked all the low hanging fruit that were
+on my mind when I started this resolution, but an unexpected culprit has also
+appeared: seasons. When I started the resolution it was still winter, and during
+the cold months it's a lot easier to stay inside and work on a computer. As the
+weather warms it's been harder to find time though, in between working on the
+garden and going out and doing things with friends.
+
+Figuring out what to write about is becoming more of a challenge as well
+(obviously, given the topic of this post). Ideally I'd like to post about things
+I'm _doing_, rather than just talking about some topic, and for the most part
+I've mostly kept to that. Constantly posting about ideas I have or opinions I
+hold isn't really contributing any real work, unless the ideas or opinions are
+really groundbreaking (they're not). If, on the other hand, I use the posts as a
+kind of background motivation to get up and do something useful, so I can write
+about what I did, then at least progress has been made on _something_.
+
+The catch there is that I've now added an additional "thing" to do every week,
+in addition to the weekly post, and, as previously covered, I just don't have
+the time and energy for that. So some posts (ahem) are pretty much fluff, and I
+barely have the energy for those! Yet another reason to wish I'd committed to 26
+in the year, I suppose.
+
+It hasn't been all added stress and strife though. Doing the posts _has_ caused
+me to work on side projects more, and even better quite a few people I know have
+given me really good feedback on what I've been doing, and some have even
+started getting involved. So, in the sense of being a way to inform others about
+the things I'm working on, the posts are a great success! And I've definitely
+been more consistent about working on side projects this year.
+
+I'll wrap this up and continue with my vacation. Summary: blog is more extra
+work than expected, it's maybe worth it, but it would be more worth it if I
+halved my pace. I'm not _going_ to halve my pace, because that's not how
+resolutions work. The end.
+
diff --git a/src/_posts/2021-05-26-viz-4.md b/src/_posts/2021-05-26-viz-4.md
new file mode 100644
index 0000000..cd6054a
--- /dev/null
+++ b/src/_posts/2021-05-26-viz-4.md
@@ -0,0 +1,213 @@
+---
+title: >-
+ Visualization 4
+description: >-
+ Birth, death, and colors.
+series: viz
+tags: tech art
+---
+
+<canvas id="canvas" style="padding-bottom: 2rem;" width="100%" height="100%"></canvas>
+
+This visualization is a conglomeration of ideas from all the previous ones. On
+each tick up to 20 new pixels are generated. The color of each new pixel is
+based on the average color of its neighbors, plus some random drift.
+
+Each pixel dies after a certain number of ticks, `N`. A pixel's life can be
+extended by up to `8N` ticks, one for each neighbor it has which is still alive.
+This mechanism accounts for the strange behavior which is seen when the
+visualization first loads, but also allows for more coherent clusters of pixels
+to hold together as time goes on.
+
+The asteroid rule is also in effect in this visualization, so the top row and
+bottom row pixels are neighbors of each other, and similarly for the rightmost
+and leftmost column pixels.
+
+<script type="text/javascript">
+
+function randn(n) {
+ return Math.floor(Math.random() * n);
+}
+
+const canvas = document.getElementById("canvas");
+const parentWidth = canvas.parentElement.offsetWidth;
+
+const rectSize = Math.floor(parentWidth /100 /2) *2; // must be even number
+console.log("rectSize", rectSize);
+
+canvas.width = parentWidth - rectSize - (parentWidth % rectSize);
+canvas.height = canvas.width * 0.75;
+canvas.height -= canvas.height % rectSize;
+const ctx = canvas.getContext("2d");
+
+const w = (canvas.width / rectSize) - 1;
+const h = (canvas.height / rectSize) - 1;
+
+class Elements {
+ constructor() {
+ this.els = {};
+ this.diff = {};
+ }
+
+ _normCoord(coord) {
+ if (typeof coord !== 'string') coord = JSON.stringify(coord);
+ return coord;
+ }
+
+ get(coord) {
+ return this.els[this._normCoord(coord)];
+ }
+
+ getAll() {
+ return Object.values(this.els);
+ }
+
+ set(coord, el) {
+ this.diff[this._normCoord(coord)] = {action: "set", coord: coord, ...el};
+ }
+
+ unset(coord) {
+ this.diff[this._normCoord(coord)] = {action: "unset"};
+ }
+
+ drawDiff(ctx) {
+ for (const coordStr in this.diff) {
+ const el = this.diff[coordStr];
+ const coord = JSON.parse(coordStr);
+
+ if (el.action == "set") {
+ ctx.fillStyle = `hsl(${el.h}, ${el.s}, ${el.l})`;
+ } else {
+ ctx.fillStyle = `#FFF`;
+ }
+
+ ctx.fillRect(coord[0]*rectSize, coord[1]*rectSize, rectSize, rectSize);
+ }
+ }
+
+ applyDiff() {
+ for (const coordStr in this.diff) {
+ const el = this.diff[coordStr];
+ delete this.diff[coordStr];
+
+ if (el.action == "set") {
+ delete el.action;
+ this.els[coordStr] = el;
+ } else {
+ delete this.els[coordStr];
+ }
+ }
+ }
+}
+
+const neighbors = [
+ [-1, -1], [0, -1], [1, -1],
+ [-1, 0], /* [0, 0], */ [1, 0],
+ [-1, 1], [0, 1], [1, 1],
+];
+
+function neighborsOf(coord) {
+ return neighbors.map((n) => {
+ let nX = coord[0]+n[0];
+ let nY = coord[1]+n[1];
+ nX = (nX + w) % w;
+ nY = (nY + h) % h;
+ return [nX, nY];
+ });
+}
+
+function randEmptyNeighboringCoord(els, coord) {
+ const neighbors = neighborsOf(coord).sort(() => Math.random() - 0.5);
+ for (const nCoord of neighbors) {
+ if (!els.get(nCoord)) return nCoord;
+ }
+ return null;
+}
+
+function neighboringElsOf(els, coord) {
+ const neighboringEls = [];
+ for (const nCoord of neighborsOf(coord)) {
+ const el = els.get(nCoord);
+ if (el) neighboringEls.push(el);
+ }
+ return neighboringEls;
+}
+
+const drift = 30;
+function newEl(nEls) {
+
+ // for each h (which can be considered as degrees around a circle) break the h
+ // down into x and y vectors, and add those up separately. Then find the angle
+ // between those two resulting vectors, and that's the "average" h value.
+ let x = 0;
+ let y = 0;
+ nEls.forEach((el) => {
+ const hRad = el.h * Math.PI / 180;
+ x += Math.cos(hRad);
+ y += Math.sin(hRad);
+ });
+
+ let h = Math.atan2(y, x);
+ h = h / Math.PI * 180;
+
+ // apply some random drift, normalize
+ h += (Math.random() * drift * 2) - drift;
+ h = (h + 360) % 360;
+
+ return {
+ h: h,
+ s: "100%",
+ l: "50%",
+ };
+}
+
+const requestAnimationFrame =
+ window.requestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.msRequestAnimationFrame;
+
+const els = new Elements();
+
+const maxNewElsPerTick = 20;
+const deathThresh = 20;
+
+let tick = 0;
+function doTick() {
+ tick++;
+
+ const allEls = els.getAll().sort(() => Math.random() - 0.5);
+
+ if (allEls.length == 0) {
+ els.set([w/2, h/2], {
+ h: randn(360),
+ s: "100%",
+ l: "50%",
+ });
+ }
+
+ let newEls = 0;
+ for (const el of allEls) {
+ const nCoord = randEmptyNeighboringCoord(els, el.coord);
+ if (!nCoord) continue; // el has no empty neighboring spots
+
+ const nEl = newEl(neighboringElsOf(els, nCoord))
+ nEl.tick = tick;
+ els.set(nCoord, nEl);
+
+ newEls++;
+ if (newEls >= maxNewElsPerTick) break;
+ }
+
+ for (const el of allEls) {
+ const nEls = neighboringElsOf(els, el.coord);
+ if (tick - el.tick - (nEls.length * deathThresh) >= deathThresh) els.unset(el.coord);
+ }
+
+ els.drawDiff(ctx);
+ els.applyDiff();
+ requestAnimationFrame(doTick);
+}
+requestAnimationFrame(doTick);
+
+</script>