Browse Source


66 changed files with 11215 additions and 0 deletions
  1. +133
  2. +3
  3. +45
  4. +344
  5. +9765
  6. +62
  7. +1
  8. +1
  9. +10
  10. +45
  11. +25
  12. +49
  13. +14
  14. +61
  15. +70
  16. +46
  17. +32
  18. +67
  19. +84
  20. +37
  21. +16
  22. +32
  23. BIN
  24. BIN
  25. BIN
  26. BIN
  27. BIN
  28. BIN
  29. BIN
  30. +13
  31. BIN
  32. BIN
  33. BIN
  34. BIN
  35. BIN
  36. BIN
  37. BIN
  38. BIN
  39. BIN
  40. BIN
  41. BIN
  42. BIN
  43. BIN
  44. BIN
  45. BIN
  46. BIN
  47. BIN
  48. BIN
  49. BIN
  50. BIN
  51. BIN
  52. BIN
  53. BIN
  54. BIN
  55. BIN
  56. BIN
  57. BIN
  58. BIN
  59. BIN
  60. BIN
  61. BIN
  62. BIN
  63. BIN
  64. +35
  65. +65
  66. +160

+ 133
- 0
.gitignore View File

@@ -0,0 +1,133 @@
# Manually added

# Created by,macos,linux,windows

### Linux ###

# temporary files which can be created if a process still has a handle open of a deleted file

# KDE directory preferences

# Linux trash folder which might appear on any partition or disk

# .nfs files are created when an open file is removed but is still being accessed

### macOS ###

# Icon must end with two \r

# Thumbnails

# Files that might appear in the root of a volume

# Directories potentially created on remote AFP share
Network Trash Folder
Temporary Items

### Node ###
# Logs

# Runtime data

# Directory for instrumented libs generated by jscoverage/JSCover

# Coverage directory used by tools like istanbul

# nyc test coverage

# Grunt intermediate storage (

# Bower dependency directory (

# node-waf configuration

# Compiled binary addons (

# Dependency directories

# Typescript v1 declaration files

# Optional npm cache directory

# Optional eslint cache

# Optional REPL history

# Output of 'npm pack'

# Yarn Integrity file

# dotenv environment variables file

### Windows ###
# Windows thumbnail cache files

# Folder config file

# Recycle Bin used on file shares

# Windows Installer files

# Windows shortcuts

# End of,macos,linux,windows

+ 3
- 0 View File

@@ -0,0 +1,3 @@
# Web base

## A base for building web things with pug, sass, and all the -ify shit

+ 45
- 0
app.js View File

@@ -0,0 +1,45 @@
var path = require('path');
var express = require('express');
var serveStatic = require('serve-static');
var reload = require('reload')
var app = express().use(serveStatic(path.join(__dirname, 'build')));
var reloader = reload(app);
app.listen(3000, function(){
console.log('Server running on 3000...');

var config = {
title: 'Spaceships in Space',
temp: './temp',
pugLayout: 'pug/layout.pug',
dest: './build',
root: './sources',
public: 'public/**',
html: 'pug/out/*.pug',
pug: 'pug',
css: 'sass/out/*.sass',
jspath: 'js',
jsname: 'app.js',
markdown: 'markdown/out/**/*.md',
all: '**'
// var libconfig = {
// dest: './build',
// root: './libsources',
// public: 'public/**',
// html: 'pug/out/*.pug',
// css: 'sass/out/*.sass',
// jspath: 'js',
// jsname: 'lib.js',
// all: '**'
// }

var builder = require('./builder')
var reload = ()=>reloader.reload();

builder(reload, config);
//builder(reload, libconfig);

+ 344
- 0
builder.js View File

@@ -0,0 +1,344 @@
var fs= require('fs');
var md = require('markdown-it')();
var del = require('del');
var path = require('path');
var through = require('through2');
var flat = require('flat-map').default;
var vinyl = require('vinyl');
var gulp = require('gulp');
var fn = require('gulp-fn');
var pug = require('gulp-pug');
var sass = require('gulp-sass');
var wrap = require('gulp-wrap');
var data = require('gulp-data');
var scale = require('gulp-scale-images');
var reduce = require('gulp-reduce-file');
var yamlMerge = require('gulp-yaml-merge');
var doMerge = require('gulp-do-merge');
var maybe = require('gulp-if');
var change =require('gulp-change');
var modify = require('gulp-modify');
var minifyCSS = require('gulp-csso');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');
var watch = require('gulp-watch');
var print = require('gulp-print').default;
var errorHandler = require('gulp-error-handle')
var debug = require('gulp-debug');
var PluginError = require('plugin-error');

var yaml = require("js-yaml");
//var gutil = require("gulp-util");

var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var sourcemaps = require('gulp-sourcemaps');

var outputFile = require('output-file')

function talk(str) {
return function() {

function moveOn(err) {

function htmlBuilder(htmlpath) {
return function builder(){
return gulp.src(htmlpath)
.pipe(print(filepath => `generated: ${filepath}`))

function markdownBuilder(){
return through.obj(function (chunk, enc, cb) {
//console.log('chunk', chunk.contents) // this should log now
cb(null, chunk)

function splitYAML(cwd){
return through.obj(function(file, enc, next){
var strings = file.contents.toString('utf8').split(/(?=%YAML)/);
var base = path.join(file.path, '..');
if (strings.length>0){
var markdown = new vinyl({
cwd: cwd,
path: path.join(base,path.basename(file.path)),
contents: Buffer.from(strings[0])
if (strings.length>1){
var yaml = new vinyl({
cwd: cwd,
path: path.join(base,path.basename(file.path,".md")+".yaml"),
contents: Buffer.from(strings[1])

var builder = function(reload, config){
function getDefaultTitle(file, ext){
var ext = ext || ".md"
var filename = path.basename(file.path, ext);
if (filename!="index"){
return path.basename(file.path, ext)
.split(/[ -]/)
.map(s=>s.charAt(0).toUpperCase() + s.slice(1))
.join(" ")
+ " - " + config.title;
} else {
return config.title
gulp.task('html', htmlBuilder(path.join(config.root, config.html)));
gulp.task('markdown', function(){
var all = []
return gulp.src(path.join(config.temp+"/**/*.md"))
.pipe(print(filepath => `working: ${filepath}`))
fileModifier: function(file, contents){
var pugLayout = fs.readFileSync(path.join(config.root, config.pugLayout),'utf8');
var depth = (file.path.split("/").length - 7);
var dots = "..";
for (var i = 0; i<depth; i++){
dots = path.join("..",dots);
var dirname = path.join("..","..","..","..","..","..",path.dirname(file.path))
var tryPath = path.join("..","temp",dirname,path.basename(file.path))
return pugLayout
.replace('[[markdown]]', tryPath)
.replace(/\[\[dots\]\]/g, dots);
var dir = path.dirname(file.path);
var filename = path.basename(file.path,".md");
var yamlPath = path.join(dir,filename+".yaml");
if (fs.existsSync(yamlPath)){
var data = yaml.safeLoad(fs.readFileSync(yamlPath, 'utf8'));
if (!data.hasOwnProperty("title")){
data.title = getDefaultTitle(file);
data.title = data.title + " - " + config.title
return data;
} else {
return {
title: getDefaultTitle(file)
p = path.join(config.root, config.pugLayout);
basedir: '/Users/Gaeel/dev/newSpaceships/sources/pug/',
pretty: true
.pipe(print(filepath => `generated: ${filepath}`))

gulp.task('css', function(){
return gulp.src(path.join(config.root, config.css))
.pipe(print(filepath => `generated: ${filepath}`))

gulp.task('js', function () {
var b = browserify({
entries: path.join(config.root, config.jspath, config.jsname),
debug: true,
transform: [babelify.configure({
presets: ['babel-preset-env']

return b.bundle()
.on('error', function(err){
.pipe(print(filepath => `bundling: ${filepath}`))
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(print(filepath => `bundled: ${filepath}`))
var scaleParameters = (file, cb)=>{
const jpegFile = file.clone()
jpegFile.scale = {maxWidth: 700, format: 'jpg'}
cb(null, [jpegFile])
gulp.task('public', function(){
return gulp.src(path.join(config.root, config.public))
return [".jpg",".jpeg",".png"].includes(path.extname(file.path));
}, scale()))
.pipe(print(filepath => `copied: ${filepath}`))

gulp.task('clean', function(done) {
del([path.join(config.dest, '*')]).then(paths => {
console.log('Deleted files and folders:\n\t'+ paths.join('\n\t'));
gulp.task('cleantemp', function(done) {
del([path.join(config.temp, '*')]).then(paths => {
console.log('Deleted files and folders:\n\t'+ paths.join('\n\t'));
gulp.task('preprocess', function(){
try {
return gulp.src(path.join(config.root, config.markdown))
.pipe(splitYAML(path.join(config.root, "markdown", "out")))
} catch (e) {
gulp.task('collecttags', function(){
return gulp.src(config.temp+"/**/*.yaml")
.pipe(debug({title: 'unicorn:'}))
(memo, file)=>{
var data = yaml.safeLoad(file.contents.toString())
var memo = memo || [];
if (data.hasOwnProperty("tags")){
for (var i = 0; i < data.tags.length; i++) {
var tag = data.tags[i];
if (!memo.hasOwnProperty(tag)){
memo[tag] = [];
url: "/"+path.relative(config.temp,file.path).replace(".yaml",".html"),
title: data.title || getDefaultTitle(file, ".yaml")
} catch (e) {
console.log("This is file: "+file)
return memo
try {
return yaml.safeDump(memo)
} catch (e) {
gulp.task('mapandtag', function(done){
var tags = yaml.safeLoad(fs.readFileSync(path.join(config.temp,"tags.yaml")).toString('utf8'))
var tagstr = "## All tags: \n"
for (var tag in tags) {
var str = "## Pages tagged with: "+tag+"\n";
if (tags.hasOwnProperty(tag)) {
tagstr+="* ["+tag+"](/tag/"+tag.replace(" ","-")+".html)\n"
for (var i = 0; i < tags[tag].length; i++) {
str+="* ["+tags[tag][i].title+"]("+tags[tag][i].url+")\n";
str+="\n[View all available tags](/tags.html)";
fs.writeFileSync(path.join(config.temp,"tag",tag.replace(" ","-")+".md"), str);
fs.writeFileSync(path.join(config.temp,""), tagstr);
gulp.task('build', gulp.parallel('markdown', 'css', 'public' ));
gulp.task('clearscreen', function(done) {
gulp.task('reload', function(done) {
console.log("Build done, going for reload...");
gulp.task('cleanbuild', gulp.series('clean', 'cleantemp', 'preprocess', 'collecttags', 'mapandtag', 'build', 'reload'));
var watcher =, config.all), gulp.series('cleanbuild'))

function wrapPipe(taskFn) {
return function(done) {
var onSuccess = function() {
var onError = function(err) {
var outStream = taskFn(onSuccess, onError);
if(outStream && typeof outStream.on === 'function') {
outStream.on('end', onSuccess);

function handleError (err) {

Array.prototype.union = function (array) {
var conc = this.concat(array);
return conc.filter((i,p)=>{
return conc.indexOf(i) == p

module.exports = builder;

+ 9765
- 0
File diff suppressed because it is too large
View File

+ 62
- 0
package.json View File

@@ -0,0 +1,62 @@
"name": "web-base",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node app.js"
"author": "Gaeel Bradshaw-Rodriguez <> (",
"dependencies": {
"@tensorflow/tfjs": "^0.11.1",
"babel-core": "^6.26.3",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babelify": "^8.0.0",
"browserify": "^16.2.0",
"del": "^3.0.0",
"express": "^4.16.3",
"flat-map": "^1.0.0",
"gulp": "^4.0.0",
"gulp-change": "^1.0.2",
"gulp-concat": "^2.6.1",
"gulp-csso": "^3.0.1",
"gulp-data": "^1.3.1",
"gulp-debug": "^4.0.0",
"gulp-do-merge": "file:../modules/gulp-do-thing",
"gulp-error-handle": "^1.0.1",
"gulp-fn": "0.0.3",
"gulp-if": "^2.0.2",
"gulp-modify": "^0.1.1",
"gulp-plumber": "^1.2.1",
"gulp-print": "^5.0.0",
"gulp-pug": "^4.0.1",
"gulp-reduce-file": "0.0.1",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.1",
"gulp-scale-images": "^1.1.3",
"gulp-sourcemaps": "^2.6.4",
"gulp-uglify": "^3.0.0",
"gulp-util": "^3.0.8",
"gulp-watch": "^5.0.0",
"gulp-wrap": "^0.14.0",
"gulp-yaml-merge": "file:../modules/gulp-yaml-merge",
"gutil": "^1.6.4",
"handlebars": "^4.0.11",
"js-yaml": "^3.12.2",
"jstransformer-markdown-it": "^2.1.0",
"jstransformer-marked": "^1.0.3",
"markdown-it": "^8.4.2",
"markdown-it-include": "^1.1.0",
"marked": "^0.6.1",
"output-file": "^2.0.0",
"plugin-error": "^1.0.1",
"reload": "^2.2.2",
"serve-static": "^1.13.2",
"through2": "^3.0.1",
"vega-embed": "^3.14.0",
"vega-lite": "^2.5.2",
"vinyl": "^2.2.0",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0"

+ 1
- 0 View File

@@ -0,0 +1 @@
rsync -avrh ./build --delete

+ 1
- 0
sources/markdown/ View File

@@ -0,0 +1 @@
Built with [Markdown](, [Pug](, and [Sass]( No Javascript is needed to view this website, but a __fuckton__ was used to create it. [Learn more about how this website was built](/meta/build-process.html)

+ 10
- 0
sources/markdown/out/blog/ View File

@@ -0,0 +1,10 @@
## In search of an *ethical software license*

%YAML 1.2
title: Ethical Software
- ethics
- blog

+ 45
- 0
sources/markdown/out/blog/ View File

@@ -0,0 +1,45 @@
## Screenshake and french fries

We can (sincerely) thank Martin Jonasson & Petri Purho for [making "Juice" a thing](, and JW Nijman for [encouraging the use of screenshake](
However, I think we're applying the letter of their message, rather than the spirit of their message.
Video games run on screens, that update high-resolution pictures at a high refresh rate in reaction to player input. This might seem a little reductive, but it's really important, because it's one of the strongest things that video games have.
Martin, Petri and JW were arguing for people to **use** that power. As a game programming teacher, I often see student projects where things are static and move in the most uninspired ways possible, and often, all it takes is a nudge for the student to understand just how much power they have. So those two talks are incredibly useful, for that first step.
### Game feel as cooking
If you boil some pasta, and eat it. It's boring as hell. Add some salt and store-bought tomato paste and you've got the beginnings of a meal. So if all you're eating is plain pasta, my advice to you is *"add salt and sauce, and your meal will be tastier!"*. But that doesn't mean adding salt and sauce makes all meals tastier, and it doesn't mean that adding **even more** salt and sauce will continue to make you meal tastier.
Salt tends to make most things tastier, within limits. Some food, like potato chips, *relies* on salt to be any good at all. But there are so many other things you can add to different meals and snacks. Spices, cheeses, herbs, oils, grains. You can also prepare all the ingredients in different ways, frying, baking, dicing, basting, marinating, and all different kinds of fermenting.
So the first step in becoming a good cook, is taking that first step of adding salt and sauce. And then you keep going.
### Why I'm being an elitist gatekeepey prick today
I saw another young dev post a trailer of a game with so much screenshake that I couldn't see what was happening. I see this *all the time*, and it makes me so sad. Vlambeer made a company off of screenshake, but that's their style, in the same way that french fries are usually very salty. But you're not Vlambeer, and not all games are french fries, and everything is super salty now and I don't think it's good for my health.
Please use other spices, I assure you, they're delicious!

### Other things you can do
If you watch the two talks above, they actually mention a whole bunch of things aside from screenshake that you can use, so go and watch them again, but here are some of my favourites.

#### Screenshake, but then make it fashion
Salt *is* good, use it, but like, in tasteful amounts, and where it makes sense. In the same way, use screenshake to add a little more punch to things, when things break or bump around, screenshake works well. Also, there are different things you can do to modulate your screenshake. Consider the direction, frequency and amplitude of the shake. Recoil works well as highly directional shake along the axis of the shooting. Small pops can benefit from tiny amplitude, high frequency shakes, whereas a big earthquake might be more of a large amplitude, low frequency rumble.

#### Particles
Use particles! Again, in tasteful amounts. Sure, that super-powerful spell or massive explosion will look great with a big shower of shiny particles, but consider also small poofs when feet touch the ground or a few quick sparks when projectiles hit surfaces, or maybe some abstract glows when items appear or are picked up? Short and sweet particle effects are one of the best ways to add a little touch of magic.

#### Tweens
If you need to make something move, make it move in a smoother way. In real life things accelerate, have mass, bounce, and all kinds of interesting motions. Tweens are a cheap and easy way to inject these kinds of motions to your simulated worlds. There are loads of tween libraries out there that will let you make things move in swoopy, bouncy ways.

#### Secondary motion
This is a fancy way of saying "make other things move". Got a big gun shot recoil animation? What if one part of the gun recoils with bit more delay, and in the middle of the recoil animation the spent cartridge pops out? Now *that's* what I call badass!
An easy way of doing this kind of thing in engines that have a physics engine included is to attach smaller objects with a rigidbody with one of the joint options to the main object. That way when the main object moves, the smaller physics-enabled objects will bounce and wobble.

#### Mix & match
Have a look at the way things behave in your favourite games. Most often, the effects you'll see will use a mix of different techniques, animations, physics enabled objects, tweens, shader effects, particle effects, all coming together to make things look just right.
Also, not everything needs a big fanfare to go with it. Some effects can be big and in your face, this is a great way to let the player know that something important is happening, but most of your effects will benefit from staying subtle and understated.

%YAML 1.2
title: Screenshake and french fries
- game design
- opinion
- blog

+ 25
- 0
sources/markdown/out/ View File

@@ -0,0 +1,25 @@
## Get in touch
* [](
* [](
* [](
* [@\_gaeel\_](

Other identities:
* [](
* [FramaGit](
* [GitLab](
* [GitHub](
* [SoundCloud](
* [Peer Tube](
* [Keybase](
%YAML 1.2
title: Contact
- contact

+ 49
- 0
sources/markdown/out/eve/ View File

@@ -0,0 +1,49 @@
## The Wrong Tool
### An EVE Online story

Gang's bored™️, we're sitting in a standing fleet that hasn't seen any action in a while, and besides, there aren't that many industrialists online, so we're not really defending much, so someone volunteers as a fleet commander. We figure we might run into some good fights if we punch out into the weird no-man's land left after last month's war.

We're doing this for shits and giggles, so the fleet commander calls for "kitchen sink" ships, just grab whatever from your hangar, it's up to you. Usually people take the ship they've been meaning to learn how to fly better, or a ship that they think looks cool but doesn't fit into any of the regular fleet doctrines.
I pick a Heretic, it's an interdictor-class ship, a specialised platform for deploying interdiction spheres. These spheres create a small pocket of space that can't be warped into, out of, or through. They're great for catching fleets as they run away, and for manipulating movements during brawls. The Heretic is also the tankiest of these ships, fit with the fine armour the Amarr Empire is known for. I figure for a kitchen sink roam, it'll be nice to have some heavy tackle to wrestle some unsuspecting ships into submission for all of the damage dealers to have an excuse to fire off their expensive ammo.
We undock, and we start to take jump gates, moving our motley crew out into the dark, abandoned skies.
After a few jumps, I start to take a look at our fleet composition, and my heart sinks. Practically all of the ships are long-range missile boats, designed for striking out at over 80 kilometres. My interdiction spheres are only useful for catching ships less than 5 kilometres from me, I'm useless, I'm dead weight.
There's a small chance I drop a few spheres to slow down a fleet that might chase us down, but we're Brave Newbies, we don't usually run away, especially not when we're just faffing about trying to find a fight…
Our scouts find us a mining crew to attack, the fleet commander positions the fleet at a nice, safe 90 kilometre range, missiles fly, mining barges die.
I sit there, looking at my interdiction sphere launcher and rocket arrays
*"Yeah, good fight…"*
This continues for over an hour; we find some more miners and blap them, we run into a medium range fleet, our commander does a really good job at keeping our missile boats tantalisingly outside of their range, keeping them keen but slowly eating into their numbers as each salvo hits the selected targets.
All this time, I'm just sitting there, following the fleet around, half considering just logging off in the middle of nowhere and try and find my way home later.
And then, as we're sitting at a gate, scouts scattered over the surrounding star systems, fleet commander pondering their next move, a voice quietly, but clearly speaks out on comms
*"Check, check, I have a Nyx on d-scan"*
A Nyx is a super-capital ship, a behemoth unto the heavens, and if it's not one of ours, it's *way* out of position. Maybe lost stranded here after last months hostilities and trying to discreetly find a way back to safety.
The commander stutters
*"Can we kill a Nyx? We can't kill a Nyx, right?"*
*"We need tackle, if we can hold it long enough, we can call for help and kill it"*
This is it, this is my moment
*"Er… I'm in a Heretic…"*
Our scouts drop combat probes and quicly find the stranded Nyx, I warp in, drop an interdiction sphere, and warp out, timing how long it takes so I can drop a new sphere moments before the previous one expires. The Nyx' targeting systems are designed for latching onto other large ships, my Heretic is too small and slippery to be caught, as long as I don't stick around too long
We don't know how long it's going to take the reinforcements to gather and arrive, so I need to maximise the time I hold this Nyx on the field.
The next 45 minutes or so are spent with me warping in, dropping a sphere, and warping out, with scouts tracking the target's movement so the spheres can be dropped right on top of it each time, the rest of the fleet scatters into the surrounding systems, feeding information about any fleet movement in the area.
An enemy fleet is fast approaching, it's a race between them and our heavy damage crew. If they gather around the Nyx, I might be able to drop one more sphere, but they surely won't let me get away again.
The Nyx' friends jump into our system, with a little less than a minute left on the last sphere I dropped, our heavy fleet is still a couple jumps out. To give them maximum time to arrive, I land *just* as the last sphere is about to expire, drop a fresh one, and immediately see my shield, beautiful Amarrian plated steel armour, and my plucky ship's hull get torn to shreds by a very upset and frustrated rescue fleet. They destroy my capsule too. I deserved it…
That last sphere holds the Nyx long enough for our dreadnoughts, battleships and actual, proper tackle ships to pounce into the fight.
As my clone wakes up in a daze back at home station, I listen in on our comms, we lose a few battleships, a whole bunch of tackle, and even one of our dreadnoughts, but we killed the Nyx.
We killed a Nyx

%YAML 1.2
title: The Wrong Tool
- eve stories

+ 14
- 0
sources/markdown/out/gaming/ View File

@@ -0,0 +1,14 @@
# Path of Exile is really good and you should play it, like right now
*Quick side note, if you're an avid Path of Exile player, a lot of what I'm going to write here might seem naïve. I'm a new-ish player, trying to share what I've enjoyed about the game so far.*
In this article, I'm going to try to explain why should play Path of Exile, even if you're not someone who's into
My boyfriend and I were looking for a game to play together a while ago, and we'd heard of Path of Exile, it's free, so we figured we'd give it a go.
As I write these words, we've both completed the main campaign three times and we're learning about some of the more advanced mechanics, trying to eke our way into some of the more elusive end-game content.

%YAML 1.2
title: Path of Exile is GOOD
- review
- path of exile

+ 61
- 0
sources/markdown/out/ View File

@@ -0,0 +1,61 @@
Hello, my name is __Gaeel&nbsp;Bradshaw‑Rodriguez__
I am a designer and programmer, particularly interested in videogames, interactivity, and technology
If you're working on something weird, fun, thoughtful, or rebellious, please [get in touch](contact.html). I'd like to help you make that thing happen!
If you'd like to support the weird, nerdy and pretty things I make and do, [head to my Patreon](

## Things
Projects I've worked on or am a part of

### [→](
![ logo](voidgarden.700w.jpg)
[]( is a project/collective that aims to develop art, events, media and stuff, all tangential to video games. [more](

### l'indécadence [→](
L'indécadence was an indie games & art party taking place during IndieCade Europe in Paris. Nine games and four musicians were on the lineup. We featured colourful and unconventional projects from around the world. Relive the event through [Anna-Céléstine's]( photo gallery [on Facebook]( [more](

### .lazr. [→](
![".lazr." text on a dark CRT screen](lazr.700w.jpg)
.lazr. is a local multiplayer arena shooter with deliciously glitchy visuals. Up to four players using standard XBox controllers face off in a number of game modes. This was made in part as a homage to Warsow "insta-gib" deathmatch games, with one-hit kills, weapon-based movement, and easy to learn, hard to master mechanics. [play](

### Prepare to Meet Thy God [→](
The title and the original idea were inspired by [this photo, titled "The Long Walk"](
Prepare to Meet Thy God is an installation where the user is equipped with protective gear and an explosive ordinance disposal kit, and sent, alone, into a room where they'll have to find and defuse a bomb. The screen of the bomb includes clues as to the process to defuse it, but also threatening and disturbing messages. A camera films the user and the bomb, recording their every movement, but for the user, they're terribly alone, with only the weight of responsibility. [more](

### The Paper Sail [→](
![A paper boat on a screen](papersail.700w.jpg)
A collaboration with [Cosmografik](, The Paper Sail is a meditative experience where the user places a small paper sail on their touch-capable device (phone, tablet, Cintiq, etc…), and then explores a quiet archipelago. They'll meet insects, fish, and occasionally, another traveller. [play](

## The Future
Projects and ideas I'm working on or looking to start

### Mind Thief [→](
![The Sentinel, emerging inside a hall of colums](thief.700w.jpg)
A virtual reality experience inspired by the [Raymarching Toolkit]( by [Kevin Watters]( and [Fernando Ramallo](
Raymarching allows for dynamic shapes that move and twist in uncanny ways, and when I started building a simple scene. [more](

## Get in touch
* [](
* <a rel="me" href=""></a>
* <a rel="me" href=""></a>
* <a rel="me" href="">@\_gaeel\_</a>

Other identities:
* [](
* [FramaGit](
* [GitLab](
* [GitHub](
* [SoundCloud](
* [Peer Tube](
* [Keybase](
%YAML 1.2
- meta
- contact

+ 70
- 0
sources/markdown/out/junk/ View File

@@ -0,0 +1,70 @@
## Buzz box project

Let's build buzz boxes, drone synths, noise machines, whatever you want to call them.

I've ordered a bunch of components I think will be useful for building such devices, mostly gleaned from schematics and projects I've found online.
The two main sources of sound I can make with these are [Reverse Avalanche Mode (RAM) Oscillators]( and [555 Oscillators](
For shaping, we can build [simple RC filters]( or [the legendary MS20 filter](
And for modulation, we're going to build a bunch of [vactrols]( (LED strapped to a photodiode), although we might need to use some OpAmps to boost the voltage sent to the LEDs if we're running them off of the RAM oscillators.

### Future plans

1. Learn to build gear so that I can pivot my career
2. Prepare the [Drone Day]( workshop at [Feral Vector](
3. Make a drone album entirely from hand-built DIY junk
4. Get rich / Die trying

### Parts

As mentioned, I ordered a bunch of junk: [Mouser order for Buzz Box 0.0](
This was my shopping list before diving in:
#### Diodes
- 1N4148

#### Capacitors
- 1uf electrolytic
- Other electrolytics up to 470uf
- 470nf
- 4.7nf
- 100nf
- 1nf

#### Transistors
- SS9018 (Best transistor for reverse avalanche oscillation)
- 2N3904
- BC558 or BC559

#### Potentiometers
- 100K
- 10K
- 4.7K

#### Resistors
- 470K
- 100K
- 10K
- 22K
- 4.7K
- 1.8k
- 220
#### OpAmps
- CA3080 (Needed for original MS20 filter, couldn't find on mouser, will look elsewhere)
- TL074
- LM13700

#### Other stuff
- Jack sockets
- 555
- A bunch of 4000-series chips
- DAC chips
- Stripboard
- LEDs (like really bright ones)
- Photoresistors (Needed for vactrols, couldn't find on mouser, replaced with photodiodes and phototransistors)

%YAML 1.2
title: Buzz box parts
- junk
- buzz boxes

+ 46
- 0
sources/markdown/out/meta/ View File

@@ -0,0 +1,46 @@
## Build process
As the footer of this website says, everything you see here rests on the shoulders of giants standing on mountains of Javascript. I'm using [Markdown](, [Pug](, and [Sass]( to create all of the content. And then a stack of over a million lines of Javascript, mostly in the form of [Gulp]( plugins, interprets these files to generate the HTML and CSS that I serve to you, the reader.

My goal when setting up this process was to have the ease of use of an easy content creation mechanism, with the flexibility of a dynamic website that modern web tools allow.
Meanwhile, I want to keep the site lightweight (for you), and to be as widely compatible as possible. At the time of writing [the main page](/), with all the images, is a 66.8KB download. It's possible to do better, maybe, but I feel like it's quick enough, even on my bad 3G connection.

### Writing
I chose to use [Markdown]( for the written content, because it's easy to use, looks nice in my editor when I'm writing, and gives me everything I need to easily write articles, lists, CVs, etc…
It's a markup language I've used a lot across various projects, and I highly recommend using it if you're looking for a simple text writing format.
I use the [Marked]( compiler as a Pug filter, which allows me to embed the compiled Markdown into a more complete website, with a fixed header and footer on each page.

### Images
![Picture from Mind Thief](/thief.700w.jpg)
I dump all images I use into a folder, where they get copied to the server, uncompressed.
In parallel, there's a filter that rescales images to 700px wide, compresses them as JPEG, and saves them alongside the original.
#### Compare
* [thief.jpg (125KB)](/thief.jpg)
* [thief.700w.jpg (25.3KB)](/thief.700w.jpg)

This means I don't have to worry about converting images manually, but still allows me to use the full, uncompressed data if I want to show off high-resolution images. The extra storage space isn't an issue for me, and this allows me to pick and choose when and where more bandwidth usage is necessary.

### Page design
The layout of the page is written in [Pug](, and styled with [Sass]( Pug is a template engine, but mostly I use it as a more flexible and easier to read HTML alternative. Pug can do things like loops, importing other files, and running some compile-time code. These are the mechanisms I use to wrap the written content with the header and footer, and insert the "Last built" mention at the bottom of the page.
The build script scans for all Markdown pages to build, and swaps in copies of `layout.pug` with a reference to the document in each one.
Apparently no-one else does this, because it was a real pain to get working, and ended up with me having to add weird `[[markdown]]` tags into the Pug code, that the builder would replace at build time.

[Sass]( is a joy to work with, I wish it could just be the new stylesheet standard. It's got variables, simple functions, macros, and all kinds of toys to easily write CSS without the otherwise inevitable hair-tearing session. If you spend any time at all writing CSS, use Sass, or something like it, to help you write CSS easier. Run it as a standalone program if you have to, but never suffer the slings and arrows of outrageous stylesheet hell ever again.

### Gulp
And then there's [Gulp]( Gulp is a system that transforms data. In most cases, it reads a bunch of files, and outputs a bunch of files, and does that any time one of the original files changes.
The best thing I can say about it is that it does what it says on the tin. Unfortunately, it's a pain to work with. No two plugins share the same way to configure them, error handling is a complete and total mess, and tutorials and documentation are written as though you *already know* what the hell a "Vinyl Stream" is.
I'm hesitant to share the code for this build system, because I know it's a mess to look at, and part of me is still not sure if that mess is just how you configure a Gulp build process, or if I've committed some terrible sin against modern Javascript frameworks.
Either way, it does actually work. Now I can write articles such as this one, refresh my browser, and if all looks right, upload the whole shebang with a simple rsync script, lovely.


If you have any questions about all of this, feel free to [get in touch](/contact.html). I'm probably not the best person to talk to about modern web software, but I feel like I've learnt a few things along the way.
If you have the know-how (or want to use my system), I recommend doing something like this. Updating my website is now super easy, and I don't have to fiddle with a CMS or authentication or anything.

%YAML 1.2
title: Build Process
- meta

+ 32
- 0
sources/markdown/out/ View File

@@ -0,0 +1,32 @@
## Nebulae

*One of my first batch of Nebulabrot renders*
A while ago, I learnt about an interesting way to picture the Mandelbrot fractal. The *traditional* way is to colour each point of the plane depending on whether or not the number associated with that point escapes, and how many steps it takes to escape.
This method instead looks at what *path* the steps take to escape, producing a sort of heat map of what paths are the most common.
The result is a quite striking display of clouds and stars as the chaotic nature of complex maths are revealed.
*Side note: I detest the term "complex maths", it scares away my students, confuses hobbyists, and makes things seem harder than they really are. Really all it means is 2D maths...*
The Mandelbrot fractal is defined by the function <span class="math">z²+c</span>, but other functions exist, and in fact, it's also possible to use multiple functions in a loop.

*Three rounds of <span class="math">z²+c</span> and one round of <span class="math">(|Re(z)|+i|Im(z)|)²+c</span>, in a loop*
Mixing the Mandelbrot function with the Burning Ship function (<span class="math">(|Re(z)|+i|Im(z)|)²+c</span>) creates something that bears a resemblance to the original Nebulabrot, but adds a layer of chaos and "wispyness".

*Ten randomly selected functions*
By randomly selecting a set of functions to loop through, we create objects that are further from the neatly patterned Nebulabrot, and start to become more chaotic and warped.

I'm looking to produce posters of these, I have a print of [the Nebulabrot from the top of this page](/nebulae/neb.png), and I'd like for other people to be able to have similar prints in their homes. I'm also writing a more in-depth article/zine with an explanation of the maths and techniques involved, understandable by anyone, even with little to no maths background.

%YAML 1.2
title: Nebulae
- nebula
- nebulae
- fractals

+ 67
- 0
sources/markdown/out/random/ View File

@@ -0,0 +1,67 @@
## Thanet Storm Damage

The British side of my family is mostly based in Thanet, and I picked up this photobook when visiting as a child.
I stumbled on it while going through some of my old stuff the other day, and I figured other people might be interested.
This photobook documents the extent of the damage due to a storm in 1978, damaging infrastructure and property in the towns of Margate, Ramsgate and Broadstairs.
You can click on the images to see higher resolution scans.

*[![Front Cover](/thanet/thanet01.700w.jpg)](/thanet/thanet01.png)*
*Thanet Storm Damage*
*Wednesday, 11th January, 1978 - A Pictoral Record*

*[![Page One](/thanet/thanet02.700w.jpg)](/thanet/thanet02.png)*
*The Clockhouse on the stone pier at Margate disappears beneath a wall of spray at high tide.*

*[![Page Two](/thanet/thanet03.700w.jpg)](/thanet/thanet03.png)*
*Buckled iron superstructure of Margate's storm-battered pier sags into the foaming waters.*

*[![Page Three](/thanet/thanet04.700w.jpg)](/thanet/thanet04.png)*
*A far cry from the summer crowds that throng the main sands at Margate as the remains of the jetty are left high and dry.*

*[![Page Four](/thanet/thanet05.700w.jpg)](/thanet/thanet05.png)*

*[![Page Five](/thanet/thanet06.700w.jpg)](/thanet/thanet06.png)*
*Giant waves swept over Margate Harbour at the height of the storm.*

*[![Page Six](/thanet/thanet07.700w.jpg)](/thanet/thanet07.png)*
*Four hundred and fifty deck-chairs were smashed to matchwood and the flotsam indicates the top of the submerged Lido pool.*

*[![Page Seven](/thanet/thanet08.700w.jpg)](/thanet/thanet08.png)*

*[![Page Eight](/thanet/thanet09.700w.jpg)](/thanet/thanet09.png)*
*Devastation at Ramsgate Harbour where the huge concrete blocks were tossed aside like toy bricks.*

*[![Page Nine](/thanet/thanet10.700w.jpg)](/thanet/thanet10.png)*
*This was the scene at Broadstairs jetty where there was a heavy damage toll to boats of all sizes*

*[![Page Ten](/thanet/thanet11.700w.jpg)](/thanet/thanet11.png)*
*It wasn't only on the seafronts that the gales took their toll. This mini car was parked in an alleyway between the Tea Caddy and Morley's coffee lounge in Broadstairs Highstreet when part of the chimney masonry tumbled down on its roof.*

*[![Page Eleven](/thanet/thanet12.700w.jpg)](/thanet/thanet12.png)*
*Part of Gladstone-road, Broadstairs, was closed all day when this giant evergreen smashed down across the busy link road between the town centre and Ramsgate.*

*[![Page Twelve](/thanet/thanet13.700w.jpg)](/thanet/thanet13.png)*
*the wrecked cafe at Joss Bay, Broadstairs.*

*[![Page Thirteen](/thanet/thanet14.700w.jpg)](/thanet/thanet14.png)*
*Catamarans and yachts were lifted like toys from their moorings and flung onto the bank at Sandwich Marina.*

*[![Page Fourteen](/thanet/thanet15.700w.jpg)](/thanet/thanet15.png)*
*The River Stour overflowed its banks and turned the marshland at Richborough alongside Thanet's pulveriser plant into a vast lake.*

*[![Back Cover](/thanet/thanet16.700w.jpg)](/thanet/thanet16.png)*
*Reprinted from the __East Kent Times__*

* *John Wilson*
* *Brian Green*
*Thanet Printing Works, Church Hill, Ramsgate.*

%YAML 1.2
title: Thanet Storm Damage
- random

+ 84
- 0
sources/markdown/out/things/ View File

@@ -0,0 +1,84 @@
## AE Modular

*[![My AE Modular system, fully patched](/audiogear/aemodular-front.700w.jpg)](/audiogear/aemodular-front.jpg)*

This article will serve more as a review than anything else.
At the time of writing, I've been the owner of a Tangible Waves AE&nbsp;Modular Starter Rack 2 for just over six months, and I'm bored of making excuses for it.
It's mostly unusable, it's not at all fun to use, it doesn't sound very nice, and it's falling apart.

### The AE Modular format
AE&nbsp;Modular is a *tiny* format (10×2.5cm modules), and the goal is to be cheap.
This format uses patchwire cables (like the ones you use in Arduino prototyping projects), inputs are grouped in the top left, outputs in the top right, and the lower three quarters of the modules have the knobs and switches. Text and indications are simply stamped onto the faceplate, the knobs are small and the switches are smaller.
Everything runs on a simple 0→5v system, audio is centered at 2.5v and oscillators have around 2v of throw.
A bus carries GND, 5v power and a few signals through the rack and to each module.
The rack is powered from a **MASTER&nbsp;I/O** module that also sends MIDI signals across the bus.
### The positives
1. It's cheap
2. It's small

That's it… At this point it's the only good I can say about it. If all you care about is *literally* just the cost and the portability of your modular system, then maybe this is for you, but I feel like too many compromises were made here.

### The downsides

I'm going to go down quite a few things here. The name, the design, the build quality, and the problems. So here goes…

#### The name
The *AE* in *AE&nbsp;Modular* stands for *Abused Electronics*. I know why they chose this name, using electronics in unintended ways is fun, but the term has connotations that make it an awkward moment when talking to people about this format.
If this was the only thing wrong with this machine it would just be an awkward side-note. However, it feels like this unpleasant choice of name reflects lack of the care and maturity of the whole system.

#### The design & build quality
The sockets for the patchwires have connectivity problems and wires pop out. I'm told the future modules have improved sockets, but my system is hard to use because of this.
There are holes drilled in the backplate to allow access to the trimming pots for the oscillators, but they're only drilled on one side of the system, so while you can move modules around, if you intend to tune your oscillators, you'll need to keep them on the left.
Some of the knobs are scratchy, so you can hear pops and crackles when tweaking values.
All of the knobs are simple potentiometers, even for modules and values that should be selectors, like the division on the **BEAT DIVIDER**.
The 0→5v range makes it incompatible with other modular formats that almost all use a -12→12v range. It is possible to use the **MASTER&nbsp;I/O** to get 2 audio ins or outs and 2 CV ins or outs, but other than that, choosing an AE&nbsp;Modular system pretty much locks you in.

#### The oscillators
As mentioned earlier, the oscillators have a trim pot on the backside for tuning. The tracking is dependant on the that pot and the front-side frequency knob, so if you ever want to shift an oscillator relative to the others, you'll need to adjust the trim pot for it to still track 1v/oct (kinda, the tracking will always be a little off, especially on the **2OSC**). What this means is that any attempt to stay in tune will require a screwdriver to adjust the trim pot each time you play with a frequency knob.

The **2OSC** module has a knob for oscillator 1 pulse width, but not for oscillator 2, you'll need to wire in a voltage to its PWM input.
The SYNC inputs silence the oscillators when their value is high, so they're useless on the **2OSC**, but they do work properly on the **VCO**.
The **2OSC** oscillators have 4 outputs each:
* Two pulses (labeled as square)
* A sawtooth
* A nothing (labeled as pulse)
The pulsewidth has a very tight range on both the **2OSC** and the **VCO**, using the full 0→5v input will cause silence on either end, so you'll need to attenuate any voltage to those inputs.

#### The filters
The two **FILTER _Wasp type_** modules are very resonant when the resonance knob is at zero, and they're extremely resonant, but terribly quiet when the knob is turned up. They do not self-oscillate.
The freq knob is only useful from just a little to the left of the 12 o'clock position and up, anything below that position has no effect on the sound.
There are two CV inputs (nice!) but only the first has an attenuator knob.

The **NYLE FILTER** module has a better behaved resonance, with a smooth ramp from calm to *screach* as the resonance is increased. This filter will self-oscillate, but it's not a particularly pleasant sound, more like a distorted noisy sine wave than the purer tones from modules you may have used elsewhere.
This time the freq knob is even weirder. From 12 o'clock upwards, it behaves mostly as you'd expect, even if above the 3 o'clock position we've already reached the maximum. However, below the 12 o'clock, the low pass is gated out, but the band and high pass channels will become silent, and then open back up and let all frequencies through after a few seconds.

#### The sequencers
None have an internal clock, so you'll need to use an LFO to clock your sequencers.
The **SEQ16** is a *mess*, you have 5 pitch knobs that you use to pick 5 voltages, plus an extra 0v for free, for a total of 6. Then you have 16 knobs to write your sequence with, picking one of the 6 voltages, or a pause, for each step. Theres no quantizer, and you can't easily pick voltages to "pre-listen" to, so finding 6 notes that form a workable scale is a pain. And the 16 knobs are linear potentiometers, not selectors, so it's very fiddly having to line up the indicator on the knob with the printed markings.
The RESET input has a different behaviour on the **STEP10**, **SEQ16** and **BEAT DIVIDER** modules, so you have to come up with fun strategies to get them all in sync.

#### Utilities
The **LFO**s don't have a CV in, but they do have PWM for some reason.
The **NOISE** has a random pulse output, but its slowest rate is still too fast for most uses.
The **DELAY** actually pushes signal back through its input, which means that simply having the **DELAY** in your patch anywhere will add echoey noise to your composition, even if the **DELAY**'s output is disconnected.

## In conclusion
This system cost me €674.73
That's the total for an **AE&nbsp;Modular Rack 2**, and extra **2VCA**, **NYLE FILTER** and **SEQ16** modules.
This is just about half the cost of the [Erica Synths Pico System II](, slightly more expensive than the [Teenage Engineering 400](, and more expensive than some semi-modular boxes like the [Make Noise 0-Coast]( If I was to make that choice again now, I don't think AE&nbsp;Modular would be the path I choose.

At first it was fun playing with my own modular system for the first time. Figuring out how each module interacts with the others, spending time getting to know my system and teasing out sounds I'd heard from other artists.
And then realising that the quality of the modules really wasn't there. Between awkard tuning, obvious missing features, and weird idiosyncrasies, this feels more like someone's DIY hobby project than a commercial musical instrument.
I was thinking about trying to build some DIY stuff for this admittedly simple format, but while it *is* simple, most, if not all, schematics for custom modules are for the more common -12→12v range.
I think I'm going to bite the bullet and sell off this unit if I find someone who wants it, and get a Eurorack system instead…

%YAML 1.2
title: AE Modular
- modular
- gear
- audio
- review

+ 37
- 0
sources/markdown/out/things/ View File

@@ -0,0 +1,37 @@
## Keyboard
*[![An ortholinear layout keyboard with orange keycaps on a desk](/keyboard/deskcrop.700w.jpg)](/keyboard/deskcrop.jpg)*
My main driver that I have set up at the Lucky Lab office space is an [OLKB Preonic]( with [Purple Zealios V2 switches]( and [DSA Keycaps in OAZ Orange from PMK](
### Design
The "ortholinear" grid layout is argued to be more comfortable to type on. The horizontal staggering of conventional keyboards supposedly cause micro-movements that can create discomfort.
I chose 62g, 65g, and 67g weights for the Zealios switches, the innermost keys use the heavier 67g switches and the lighter 62g ones are for the outermost columns. This allows for a lighter keystroke under my pinky finger and heavier under my index, evening out the perceived weight.
### Feel
I only have my own anecdotal evidence to go by, and my current view is that the ortholinear layout has little to no advantage, but I've gotten used to it so I'm comfortable with it now.
The Zealios switches are really nice switches. I got them as a quieter compromise to clicky switches, but I've come to prefer them. As for the weight gradient, I actually feel like it leads to a smoother typing experience, I'm more confident in using my pinky fingers.
### Build
*[![Two PCBs with gold wiring and black solder mask](/keyboard/pcb.700w.jpg)](/keyboard/pcb.jpg)*
The Preonic is a DIY build, you need to source keycaps and switches yourself. You'll need a soldering iron and about an hour or two ahead of you. Because switches are fairly large components and the layout is straightforward it's a very easy project for soldering beginners, so I highly recommend this if you're new to soldering and want to practice.

The Preonic kit has a top plate, which stabilises the switches, a PCB that the switches are soldered to and that contains all the logic and connectivity, and a bottom plate that protects the underside of the keyboard and features a lovely plant illustration by [Katie Doodles](

Because the main components are plastic or PCB, the finished build is very lightweight, easy to pop into a sleeve and travel with.
The open "sandwich" design does mean the case doesn't directly prevent dust or other contaminants from getting into the keyboard, it's not been an issue for me yet, and it's easy enough to take apart and clean should that become a problem in the future.
*[![Katie Doodles' illustration in gold on black on the underside of the keyboard](/keyboard/illustration.700w.jpg)](/keyboard/illustration.jpg)*
*Katie Doodles' illustration on the underside of the keyboard*

*[![The keyboard in context at a desk](/keyboard/desk.700w.jpg)](/keyboard/desk.jpg)*
*My desk at the Lucky Lab office space in Lille*

*[![Close up of the grid AZERTY layout](/keyboard/keyboard.700w.jpg)](/keyboard/keyboard.jpg)*
*French AZERTY layout, raise and lower keys to access other layers*

*[![Close up of a Zealio switch](/keyboard/zealios.700w.jpg)](/keyboard/zealios.jpg)*
*Purple Zealio keyswitch*

%YAML 1.2
title: Keyboard
- keyboard
- gear
- lucky lab

+ 16
- 0
sources/markdown/out/things/ View File

@@ -0,0 +1,16 @@
## Noisy Boys
I've gotten into modular synthesis recently, and I'd like to build modules and parts, based on weird ideas I have for sound generation, or a fascination with maths.

### Pisano sequencer
Pisano sequences are variants of the world-famous Fibonacci sequence, the only difference is the choice of the first two numbers. An interesting property of these sequences, is that you can take the remainder of the division of each number in the sequence by some other number, and you get a neatly repeating sequence of numbers. Because addition and modulo are simple operations that can be implemented in hardware, I'd like to build a simple clocked sequencer that use these properties.

### Physical slew rate limiter
Faders can be used to give a user control over a voltage in a system. Motorised faders allow the system to take control of said fader.
Because the motor, especially if starved of power, takes some amount of time to move the fader to the desired position, the voltage will slew, which can be an interesting effect for creating envelopes and motion in a synth patch. The module would then have a voltage input, and a comparator circuit that tried to drive the motorised fader to match the input voltage.
Finally, because the slew rate limiter is based on a physical fader, the musician can move and manipulate the output directly, adding human performance to the automated function.

%YAML 1.2
title: "Noisy Boys"
- modular

+ 32
- 0
sources/markdown/out/things/ View File

@@ -0,0 +1,32 @@
## Olegtron 4060 MK2

*[![Click to view demo video](/audiogear/olegtron4060still.700w.jpg)](/audiogear/olegtron4060.mp4)*
[View a short demo video](/audiogear/olegtron4060.mp4)

The Olegtron 4060 MK2 is a handheld signal generator based around the CD4060 CMOS chip, and it's weird as fuck.

It's [available from Olegtron for €119]( and if you like noise or drone music, weird electronics, or just cool toys that make cool noises, I totally recommend it.

It can be powered from a 9v battery, a wall wart, or by wiring a voltage differential to the power section. Depending on the position of a jumper, the Olegton will output a unipolar full-voltage, unipolar half-voltage or bipolar full-voltage signal. This makes it compatible with most applications you might want to throw at it, headphone audio at ±4.5v from the 9v battery, Arduino friendly signals at +3v powered straight from the Arduino board, or a whopping ±15v powered from a wall wart for driving motors.

### Operation
The way you use the 4060 is you connect electronic components to the big patch bay. There are no wrong ways to do this. The 4060 will happily continue to output a flurry of signals with pretty much no matter how you wire it up. Connect resistors, diodes, capacitors, LEDs, vactrols, whatever! Different components warp the output in different ways, and while you can move randomly and always get interesting sounds, eventually you begin to recognise certain patterns with certain components. Diodes and LEDs cause stuttering, transistors jumble up the sequence, and capacitors make sweeps and clicks.

The two big knobs affect frequency and current. The frequency knob affects the "pitch" of the device. At the lowest setting you get semi-structured clicks and blips, and at the highest you get a loud drone. Anything in between is a landscape of harsh textures and wild sequences.

### Social patching
You can connect up to 4 stereo 2.5" jacks, so the 4060 is also a fun social experience. Play with a friend, taking turns adding or removing components.
The flashing lights and weird look makes it great nerd bait. I brought the 4060 to FOSDEM and was sat at the cafeteria playing with a friend, other people were curious and wanted to have a go. It was a pleasant experience, jamming with strangers, that I will attempt to recreate at other events I go to.

### Using with other gear
Because the 4060 accepts anything from 3v to 15v for power, and has some flexibility regarding its output voltages, it's easy to connect it to other devices or synths, and respect their expected values. I connected it to [my AE Modular](/things/ae-modular.html) a few times, and the 4060 turned it into a rowdy little machine. [Listen to some of the weirdness: killdest.ogg](/audiogear/killdest.ogg)

%YAML 1.2
title: Olegtron 4060 MK2
- gear
- modular
- audio
- review

sources/public/audiogear/Erf.ogg View File

sources/public/audiogear/aemodular-front.jpg View File

Before After
Width: 4160  |  Height: 3120  |  Size: 5.5 MiB

sources/public/audiogear/aemodular-side.jpg View File

Before After
Width: 4160  |  Height: 3120  |  Size: 4.8 MiB

sources/public/audiogear/killdest.ogg View File

sources/public/audiogear/olegtron4060.mp4 View File

sources/public/audiogear/olegtron4060still.jpg View File

Before After
Width: 720  |  Height: 720  |  Size: 31 KiB

sources/public/favicon.ico View File

Before After

+ 13
- 0
sources/public/icon.white.svg View File

@@ -0,0 +1,13 @@
<svg class="vector" width="300px" height="300px" xmlns="" baseProfile="full" version="1.1" style="fill:none;stroke:white;stroke-width:28px;stroke-linecap:square;">
<g transform="translate(0,30)">
<g transform="translate(150,150),rotate(120,0,0)">
<path d="M0,-60 a60,60 0 1,0 0,120 l100,0"></path>
<g transform="translate(150,150),rotate(240,0,0)">
<path d="M0,-60 a60,60 0 1,0 0,120 l100,0"></path>
<g transform="translate(150,150),rotate(0,0,0)">
<path d="M0,-60 a60,60 0 1,0 0,120 l100,0"></path>

sources/public/keyboard/desk.jpg View File

Before After
Width: 900  |  Height: 1200  |  Size: 141 KiB

sources/public/keyboard/deskcrop.jpg View File

Before After
Width: 785  |  Height: 686  |  Size: 190 KiB

sources/public/keyboard/illustration.jpg View File

Before After
Width: 1200  |  Height: 900  |  Size: 217 KiB

sources/public/keyboard/keyboard.jpg View File

Before After
Width: 1099  |  Height: 456  |  Size: 104 KiB

sources/public/keyboard/pcb.jpg View File

Before After
Width: 1200  |  Height: 900  |  Size: 207 KiB

sources/public/keyboard/zealios.jpg View File

Before After
Width: 1200  |  Height: 900  |  Size: 121 KiB

sources/public/lazr.png View File

Before After
Width: 3360  |  Height: 474  |  Size: 734 KiB

sources/public/markdown.png View File

Before After
Width: 918  |  Height: 264  |  Size: 65 KiB

sources/public/nebulae/chaos.png View File

Before After
Width: 4000  |  Height: 4000  |  Size: 33 MiB

sources/public/nebulae/eight-comp.png View File

Before After
Width: 8000  |  Height: 8000  |  Size: 100 MiB

sources/public/nebulae/eight-raw.png View File

Before After
Width: 8000  |  Height: 8000  |  Size: 127 MiB

sources/public/nebulae/mmmp1200.png View File

Before After
Width: 1200  |  Height: 1200  |  Size: 2.2 MiB

sources/public/nebulae/neb.png View File

Before After
Width: 4800  |  Height: 4800  |  Size: 34 MiB

sources/public/papersail.png View File

Before After
Width: 2000  |  Height: 672  |  Size: 1.1 MiB

sources/public/stuff/Trans-Resources.png View File

Before After
Width: 1541  |  Height: 830  |  Size: 338 KiB

sources/public/thanet/thanet01.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 6.4 MiB

sources/public/thanet/thanet02.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 8.8 MiB

sources/public/thanet/thanet03.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 7.0 MiB

sources/public/thanet/thanet04.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 5.9 MiB

sources/public/thanet/thanet05.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 8.8 MiB

sources/public/thanet/thanet06.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 7.4 MiB

sources/public/thanet/thanet07.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 9.5 MiB

sources/public/thanet/thanet08.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 9.3 MiB

sources/public/thanet/thanet09.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 7.2 MiB

sources/public/thanet/thanet10.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 8.2 MiB

sources/public/thanet/thanet11.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 7.0 MiB

sources/public/thanet/thanet12.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 7.8 MiB

sources/public/thanet/thanet13.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 6.8 MiB

sources/public/thanet/thanet14.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 8.6 MiB

sources/public/thanet/thanet15.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 7.3 MiB

sources/public/thanet/thanet16.png View File

Before After
Width: 2464  |  Height: 1728  |  Size: 6.3 MiB

sources/public/thief.jpg View File

Before After
Width: 1920  |  Height: 908  |  Size: 123 KiB

sources/public/voidgarden.png View File

Before After
Width: 842  |  Height: 277  |  Size: 22 KiB

+ 35
- 0
sources/pug/layout.pug View File

@@ -0,0 +1,35 @@
include [[dots]]/sources/pug/utils
doctype html
title= title
meta(http-equiv="X-UA-Compatible" content="IE=edge")
meta(name="viewport" content="width=device-width, initial-scale=1")
link(rel='stylesheet', href='/style.css')
h1 Spaceships in Space
span navigation(
a.hlink(href="/") home
a.hlink(href="/tags.html") tags
a.hlink(href="/contact.html") contact
| )
include:marked [[markdown]]
h4 Related tags
if tags
each tag in tags
a(href="/tag/"+tag.replace(" ","-")+".html")= tag
p Last built: #{formatDate(new Date())} - #{formatTime(new Date())}
include:marked [[dots]]/sources/markdown/

+ 65
- 0
sources/pug/utils.pug View File

@@ -0,0 +1,65 @@
var cache = [
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' '

function leftPad (str, len, ch) {
// convert `str` to a `string`
str = str + '';
// `len` is the `pad`'s length now
len = len - str.length;
// doesn't need to pad
if (len <= 0) return str;
// `ch` defaults to `' '`
if (!ch && ch !== 0) ch = ' ';
// convert `ch` to a `string` cuz it could be a number
ch = ch + '';
// cache common use cases
if (ch === ' ' && len < 10) return cache[len] + str;
// `pad` starts with an empty string
var pad = '';
// loop
while (true) {
// add `ch` to `pad` if `len` is odd
if (len & 1) pad += ch;
// divide `len` by 2, ditch the remainder
len >>= 1;
// "double" the `ch` so this operation count grows logarithmically on `len`
// each time `ch` is "doubled", the `len` would need to be "doubled" too
// similar to finding a value in binary search tree, hence O(log(n))
if (len) ch += ch;
// `len` is 0, exit the loop
else break;
// pad `str`!
return pad + str;
function formatDate(date) {
var monthNames = [
"January", "February", "March",
"April", "May", "June", "July",
"August", "September", "October",
"November", "December"

var day = date.getDate();
var monthIndex = date.getMonth();
var year = date.getFullYear();

return day + ' ' + monthNames[monthIndex] + ' ' + year;
function formatTime(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
return leftPad(hours, 2, 0) + ':' + leftPad(minutes, 2, 0) + ":" + leftPad(seconds, 2, 0);

+ 160
- 0
sources/sass/out/style.sass View File

@@ -0,0 +1,160 @@
// outline: solid 1px red !important
// transition: none !important

$bhue: 180
$background-color: hsl($bhue, 0, 99%)
$text-color: hsl($bhue+180, 0, 5%)
$link-color: hsl($bhue+180, 100, 25%)
$title-color: hsl($bhue+180, 0, 50%)
$hover-color: hsl($bhue+180, 100, 50%)
$page-width: 15cm

margin: 0
font-family: -apple-system,system-ui,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,'Helvetica Neue',Arial,sans-serif
background: $background-color
color: $text-color

//text-align: justify
font-size: 13pt
line-height: 1.3
margin-bottom: 1cm

height: 0
border-bottom: 0

#thepage, #footer, #header
//background-color: $panel-color
max-width: $page-width
margin: auto
padding: 1cm
padding-top: 2cm
padding: 0 1cm
margin-top: -3.5mm

max-width: 100%

text-decoration: none
color: $link-color

p > a:after
position: relative
content: "°"
margin-left: -0.05em
font-size: 75%
top: -0.3em

// p a:last-child::after
// content: unset

.linkheader a::after
content: unset

color: $hover-color

h1, h2, h3, h4, h5, h6
color: $title-color
h1, h2,
font-weight: 800
padding: 0.2em
border-bottom-style: solid
border-bottom-width: 1px

h3, h4, h5, h6
//margin-left: 1.5em
margin-bottom: -0.25em
font-weight: 800

h4, h5, h6
margin-bottom: -0.5em
h5, h6
margin-left: 1.5em
font-size: 15px
text-indent: 0em
font-weight: 800
display: inline-block

display: inline-block

color: $title-color
margin: 0px

padding: 0 3px
.webring > img
width: 1em
height: 1em
margin: -0.15em
filter: brightness(20%) sepia(100%) hue-rotate(-50deg) saturate(500%)

.webring:hover > img
filter: brightness(35%) sepia(100%) hue-rotate(-50deg) saturate(500%)
@media (max-width: 18cm)
text-align: left

@media (max-width: 13cm)
max-width: 100%
width: 100%
.webring > img
width: 1em
height: 1em
#thepage, #footer, #header
padding: 0 0
margin: auto
width: 90%
max-width: unset

@media (max-width: 10cm)
//text-align: justify
font-size: 12pt
font-size: 18pt
margin-top: -3mm
@media (max-width: 8cm)
font-size: 10pt
display: none
margin-top: 0
#footer p
font-size: 11pt
font-family: serif
font-style: italic
padding: 0 1px