EU flag minus UK star


I think, therefore I am.


I split my time between three universities:

De Montfort University (DMU) where I've been the longest, and have taught various modules such as front-end web development, functional programming, and research to a variety of students from different courses across the Faculty of Computing, Engineering, and Media.

The University for the Creative Arts (UCA) where I'm remote teaching a module on web design & development and I'm part time Link Tutor for Computer Science and Data Analytics.

And finally, I'm external examiner at the University of Hertfordshire (UH).

I'm an artist as well (Generative Art) - most recently exhibited in January 2024.

Occasionally, I also spend time programming for Ecovisum.

I'm a transdisciplinary creative generalist. I have passion for what I do and what I think is right. Sometimes that involves saying "no".

My sense of purpose is my own - I'm an introvert.

Row of order 8 magic squares


I have a PhD in Software Engineering/Computer Science. The title of my thesis is Algorithmic Meta-Creativity and it's available as HTML or PDF at

My research interests revolve around studying human and computer creativity and how they are evaluated, the absurd pseudo philosophy pataphysics and its applications, and the development of creative exploratory search algorithms inspired by pataphysical concepts.

Although the PhD wasn't classed as "practice-based" research, an artefact was submitted alongside the thesis. The software for this was written (mostly) in Python.


Internal: Home About Portfolio Images Haskell Cheats
External: GitHub Teaching Glitch CodePen Dave


Code and Art Projects

Magic Squares
Classes of magic squares shown in overlapping sets

Magic Squares

The magic square app can visualise the magic line in various different artistic styles.

The magic lines are drawn using SVG.

Node, intersection observers, service workers, Couch backend.

Also uses Haskell algorithms for generation of sets.

Franz Marc 2.0
Franz Marc 2.0

Franz Marc 2.0

Franz Marc's 'Reh im Klostergarten' from 1912
in digital form.

The hand-crafted scalable vector graphics allow for an artful explosion of colour and can be experienced interactively or meditatively.

There are 4 key modes: Paint Yourself, Solid Colours, Gradient Colours, and Automatic Transitions.

Basheau Poems
Screenshot showing interface of Basheau Poems

Basheau Poems

Basheau Poems is a mashup of Bashō's haikus and Quenueau's hundred thousand billion poems mechanism.

It is a fully responsive website hosted on my own server.

A prototype can be found on codepen.

Magic Carpet Visualiser
Magic carpet and magic square highlighted within

Magic Carpet Visualiser

A (theoretically infinite) magic carpet can be constructed from a single magic square of a special kind of class. Demonstrates magical properties of carpet and draws the magic line of any magic square within.

Uses HammerJs, SVG, and JavaScript. Source code can be found on GitHub.
Screenshot of sonnet called "conspiracy"

Pataphysics WTF

The website is a poetic search engine produced as part of wider PhD research.

The app is written in Python and uses Natural Language Processing, WordNet, various APIs, and custom written search algorithms.

Art image by Sally Wilson

Dr Physics WTF

This website:, is an online version of my PhD thesis (pdf).

The original thesis was written fully in LaTeX, and the conversion to HTML was partially completed with Pandoc but then checked and amended by hand.

gallery screenshot
Classic three panel sliding gallery

Responsive Gallery Template

Source code on GitHub.

Fully responsive, easy-to-use-code using CSS variables, optimised for touch (HammerJS).

presents screenshot
Screenshot of How-to presentation

Fania presents

A blank reveal presentation that loads in an external markdown file via the url (hosted on GitHub).

This project was created to make lecturing easier.

Source code.

Screenshot of image gallery
Screenshot of image gallery on

Image Gallery

The handwritten image gallery on my personal homepage is based on responsive CSS grids, CSS filters, lazyloading, and efficient modals.

Source code.

Screenshot of Personal Space
Screenshot of Personal Space

Personal Space

An online artwork extracting individual diary entries containing words selected according to the intensity of live space weather data.

This was a collaboration between Alice Tuppen-Corps, Dave Everitt and myself.

Basic sentiment analysis and natural language processing in JavaScript, source code.

Screenshot of Prime Factorisation Chart
Screenshot of Prime Factorisation Chart

Prime Factorisation Chart

Very simple but beautiful maths based HTML/CSSOM/JavaScript artwork using random colours. Sourcecode on GitHub.

Screenshot of Barefoot Gen Manga Reader Site
Screenshot of Barefoot Gen Manga Reader Site

Barefoot Gen Manga Reader Site

I created this page because I realised there was no source to view the whole manga (all 10 volumes) online conveniently.

It's a simple HTML/CSS/JS with a bit of Hammer and PWA to make it nice on devices.

The scans are taken from

Screenshot of Hangeul Cheatsheet
Screenshot of Hangeul Cheatsheet

Hangeul Cheatsheet

A cheatsheet for Korean alphabet (Hangeul) reference because why not.

It's simple HTML/CSS/JS with some Hammer and PWA thrown in.

Source code available at GitHub/fania/hangeul.

Screenshot of Kittens-R-Us
Screenshot of Kittens-R-Us


This was a responsive, browser, local-storage shop demo for computer science students.

Source code.

Screenshot of LocalStorage Hearts
Screenshot of LocalStorage Hearts

LocalStorage Hearts

This was another very small browser, local-storage like-button demo for computer science students.

Screenshot of Multi-coloured SVG line with Dasharray
Screenshot of Multi-coloured SVG line with Dasharray

Multi-coloured SVG line with Dasharray

This is demo of how to create multi-coloured SVG lines using a magic square example. Uses a similar technique to animation (e.g. Very simple SVG Line Animation) but produces a better/different look to masking (e.g. Multi-coloured SVG line with Mask).

Solutions Log

Inspired by the infinite solutions log

DEAR PEOPLE FROM THE FUTURE: Here's what we've figured out so far ...



Core commands:

  • Create repository in folder git init
  • Clone existing repository git clone URL
  • Add any new files git add .
  • Commit changes git commit -m 'Message'
  • Push changes git push origin master or git push
  • Force push changes (overwrites stuff) git push --force
  • List all local branches git branch
  • List all branches (incl. remote) git branch -a
  • Create branch (but don't checkout) git branch NAME
  • Create branch (and checkout) git checkout -b NAME
  • Switch branch git checkout NAME
  • Delete local branch git branch -d NAME
  • Force delete local branch git branch -D NAME
  • Delete remote branch git push origin --delete NAME
  • Work on two branches at the same time (in VS Code):
      from master branch do this: git branch BRANCH-NAME and then git worktree add NAME-of-FOLDER BRANCH-NAME
      Git Worktree Add Example afterwards remove with git worktree remove BRANCH-NAME
  • See log git log
  • Nicely formatted log log --graph --date=short --pretty=format:"%h%x09%ad%x09%s"
  • Purge file from commit history with BFG Repo-Cleaner
  • Remove "ignored" files that were added before the gitignore was created git rm -rf --cached . and then git add . (make sure gitignore is uptodate)
  • Delete "cached" files that were previously deleted from cache but are still in history git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch "FILENAME"' -f HEAD (DANGEROUS - MAKE BACKUP)
  • Fix diverged branch problem: check git log HEAD..origin/master, then if head is the same (which indicates that indeed branches have diverged at some earlier commits) then git merge origin/master and git commit -m "message" and git push

Git Deployment

January 2020

1. On local machine

  • add ssh key to server's authorised keys ssh-copy-id -i ~/.ssh/ user@server
  • add ssh key to server's authorised keys ssh-copy-id -i c:\users\name\.ssh\ user@server
  • these might need a port too -p 1234

2. On server

  • live site: /home/user/site
  • bare repo: /home/user/site.git
  • initialise bare repo: git init --bare
  • alternatively use: git init --shared --separate-git-dir code.git html inside parent folder
  • enter hooks folder: cd hooks
  • create "post-receive" file: touch post-receive
  • add the following script using nano post-receive:
    git --work-tree=/home/user/site --git-dir=/home/user/site.git checkout -f
  • or use the post-receive script mentioned in this article: Simple automated GIT Deployment using GIT Hooks
  • fix permissions of post-receive script: chmod +x post-receive

3. On local machine

  • add new remote: git remote add server user@server:site.git
  • add new remote (with port including via git config file setup): git remote add server ssh://user@server/full/path/to/site.git
  • check remotes: git remote -v
  • add the following lines to the local git config file (i.e. in .git/config as opposed to the global config in c:\users\name\.gitconfig):
      pushall = !git push origin master && git push server master
  • make changes and push to both remotes: git pushall

Specify port:

  • git remote set-url server ssh://user@server:port/absolute/path/to/site.git
  • (if cmd complains about caching key and you can't click yes, run putty itself so you can click yes there)

Multiple users pushing to same repo on server:

  • Create user account for "git": sudo adduser git
  • su git
  • cd
  • mkdir .ssh && chmod 700 .ssh
  • touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys
  • Each user wanting to push to the server needs to add their public ssh key to the "git" user's authorized_keys file:
  • From local machine: ssh-copy-id -i ~/.ssh/ git@server
  • From local machine: ssh-copy-id -i c:\users\name\.ssh\ git@server
  • see above
  • then either have the code live in git home directory and all users push to that as git user (ssh://git@server/...)
  • or the code lives in a user home directory and other users of the same git group can push to it (ssh://user@server/...)
  • make sure folder permissions are correct (775/755 for folders/subfolders)

More info


Pandoc & Git Hooks

Add the following code to a pre-commit hook in the .git/hooks folder of your repo. This will generate html files from markdown content every time just before you commit.

Make sure you add styles inside a <style> block to a file called "styles". See pandoc command in script below.


echo "Converting markdown files to html with Pandoc..."

for file in markdown/*.md 
  echo "Processing $file"
  name=$(basename ${file%%.*})
  # echo $name

  pandoc $file -f markdown -t html -o html/$name.html -s -H styles --quiet
  # --quiet = suppress warning messages
  # -s = standalone (full htmlm not snippet)
  # -H = include file in header

git add .
echo "... and stage generated html files."


August 2019

Secure Sockets Layer

Nginx + Debian Stretch 9

  • certbot for Nginx on Debian 9 (Stretch)
  • install certbot: sudo apt-get install certbot python-certbot-nginx -t stretch-backports
  • create certificate: sudo certbot --nginx
  • certificates are found in: /etc/letsencrypt/live
  • check certificates: certbot certificates
  • renew certificates test:certbot renew --dry-run
  • force renew certificates certbot renew --force-renewal
  • expand/update certificates certbot certonly --cert-name -d,
  • revoke/delete certificates certbot revoke --cert-path /etc/letsencrypt/live/CERTNAME/cert.pem
  • check crontab: crontab -l
  • edit crontab: crontab -e
  • restart cron: sudo /etc/init.d/cron restart
  • certbot logs: /var/log/letsencrypt/letsencrypt.log
  • cron job for auto-renew: 0 0,12 * * * python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew (unconfirmed)

Nginx + Debian Jessie 8

  • Forum autorenew question
  • certificates are found in: /etc/letsencrypt/live
  • wget
  • chmod a+x certbot-auto
  • check certificates: /etc/certbot-auto certificates
  • might need to add flag:--no-bootstrap
  • renew certificates test:sudo /etc/certbot-auto renew --dry-run
  • cron job for auto-renew: 0 0,12 * * * python -c 'import random; import time; time.sleep(random.random() * 3600)' && /etc/certbot-auto renew --pre-hook "service nginx stop" --post-hook "service nginx start"

fix ssl labs server rating ubuntu nginx link


August 2019


  • pm2
  • pm2 GitHub source
  • pm2 Ecosystem File
  • Running Your Python Script with PM2
  • Install pm2: npm i -g pm2
  • Start a process via custom config: pm2 start app.json
      "name": "name-of-app",
      "script": "name-of-script.js",
      "cwd": "./",
      "watch": true,
      "ignore_watch": ["node_modules", "logs", ".git", "data"],
      "error_file": "./logs/pm2errors.log",
      "out_file": "./logs/pm2out.log",
      "log_date_format": "YYYY-MM-DD HH:mm Z"
  • Start a process: pm2 start app.js
  • List all processes: pm2 list
  • Setup startup script: pm2 startup
  • Stop a process: pm2 stop NUM/NAME
  • Restart a process: pm2 restart NUM/NAME
  • Delete a process: pm2 delete NUM/NAME
  • Forever
  • Example conf file for Forever
  • Running Node Apps with Forever
  • See also: forever-service
  • Install Forever: npm i -g forever
  • List all processes: forever list
  • List all configs: forever config
  • Start a process: forever start app.js
  • Start a process with logs: forever start -l foreverLog.log -o foreverOutput.log -e foreverErrors.log app.js
  • Stop a process: forever stop app.js
  • Restart a process: forever restart app.js
  • Start a process via forever.json: forever start forever.json
        "uid": "name-of-node-app",
        "append": true,
        "watch": true,
        "watchIgnore": ["/absolute/path/logs",
        "colors": true,
        "script": "server.js",
        "sourceDir": "/absolute/path",
        "logFile": "/absolute/path/logs/forever.log",
        "outFile": "/absolute/path/logs/foreverout.log",
        "errFile": "/absolute/path/logs/forevererror.log"
        "uid": "name-of-python-app",
        "append": true,
        "watch": false,
        "colors": true,
        "script": "",
        "command": "venv/bin/python",
        "sourceDir": "/absolute/path",
        "logFile": "/absolute/path/logs/forever.log",
        "outFile": "/absolute/path/logs/foreverout.log",
        "errFile": "/absolute/path/logs/forevererror.log"
  • Add to git post-receive hook: forever restart uid-of-process
  • Might need to make sure you run these on the same user.

Server Management

June 2022



  • Chmod Calculator
  • Get all users getent passwd
  • Get normal users getent passwd {1000..60000}
  • Change file permissions chmod XXX file
  • Escalate to root user sudo su
  • sudo adduser name
  • Escalate to "user" user su user
  • Change user permissions chown user file
  • Change permissions recursively chown -R user:group folder
  • Change group permissions chgrp group file
  • Change group permissions for all chgrp -R group folder
  • Edit access rights nano /etc/ssh/sshd_config
  • Restart ssh client after change /etc/init.d/ssh restart
  • Add user to sudo group usermod -aG sudo username


  • /etc/nginx/sites-available
  • /etc/nginx/sites-enabled
  • /etc/nginx/snippets
  • stop nginx: service nginx stop
  • restart nginx: service nginx restart
  • get nginx status (incl pid): service nginx status
  • test settings: nginx -t
  • create symlink from sites-available to sites-enabled: ln -s /etc/nginx/sites-available/name /etc/nginx/sites-enabled/name
  • Nginx not restarting
  • check running processes of nginx: ps aux | grep [n]ginx
  • kill process: kill -9 1234 for each process!
  • then start nginx again: service nginx start


  • show all running screens: screen -ls
  • start new screen with name: screen -S name
  • attach to a running session: screen -x
  • attach to session name: screen -r name
  • detach from a session: Ctrl-a d
  • kill a screen: screen -S NUMBER -X quit


show ports used: netstat -plutn


Command-line Interfaces

June 2022



Open at location

  • 'Shift-right-click' folder to open CMD there
  • Use the 'Open-In-Terminal' script

Finding things

  • Show all locations of file type -a NAME
  • Show location of file or folder which NAME
  • Show location of file or folder (if virtualenvwrapper-win is installed) whereis <NAME>


  • Reload terminal source ~/.bash_profile
  • Reload terminal source ~/.bashrc
  • Reload cmd SET PATH=%PATH%;C:\CmdShortcuts (unconfirmed)

Running Scripts

  • Make sure Execution Policy is set appriately Set-ExecutionPolicy RemoteSigned
  • Check Execution Policy with Get-ExecutionPolicy
  • See How to allow scripts to run


March 2023

  • Show invisible files Command + Shift + .


March 2023

Jekyll deployment on private server

  • Install Jekyll on your local machine and setup the project as you want.
  • We want to be pushing only the _site folder with the compiled HTML files to the server, so we don't have to install Jekyll and recompile there, as that just introduces the possibility of errors in the chain of actions.
  • Prepare the server infrastructure ready according to instructions above.
  • Then on your local machine, you need to make you Jekyll project (from now I will call this the root folder) a git repository.
  • You also need to make your _site folder a git repo.
  • In the root repo, create a pre-commit file with the following contents:
    cd absolute/path/to/Jekyll/_site
    date +'%d/%m/%Y'
    git add .
    git commit -m "Changes from $var via auto-commit script."
  • Make sure this file is executable chmod +x pre-commit
  • We also need a pre-push file:
    cd absolute/path/to/Jekyll/_site
    git push server master
  • Again, make sure the file is executable, using the same command above chmod +x pre-push
  • To make the pre-push file work, we need to make sure the git remote for the _site repo is set correctly.
  • So, check it using git remote -v
  • If it doesn't list a "server" remote then we need to add it.
  • Add the remote inside the _site repo: git remote add server ssh://user@server/full/path/to/site.git
  • At this point, the workflow is ready. You can commit work in your root folder and it gets automatically commited as well in your _site folder. When you are ready to push your source code (to GitHub usually) then your compiled code will also be pushed to the private server of your choice.


Images & Video

  • Image file type and format guide
  • Responsive Images Overview
  • Get Pixel Device Ratio: window.devicePixelRatio
  • ImageMagick Anatomy of the Command-line
  • Convert from png to webp: magick input.png output.webp
  • Use 'mogrify' to convert serveral images from jpg to webp: magick mogrify -format webp *.jpg
  • Install 'ffmpeg': brew install ffmpeg
  • Use 'ffmpeg' to convert from gif to webp: ffmpeg -i file.gif -c vp9 -b:v 0 -crf 40 file.webm
  • Use 'ffmpeg' to convert from gif to mp4: ffmpeg -i file.gif -movflags +faststart -pix_fmt yuv420p \ -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" file.mp4
  • Use 'ffmpeg' to convert from mov to webm: ffmpeg -i -c:v libvpx -pix_fmt yuva420p -b:v 2M -auto-alt-ref 0 output.webm
  • Use 'ffmpeg' to convert from mov to mp4: ffmpeg -i -vcodec h264 -acodec mp2 output.mp4






  • Reference SASS
  • How to setup Sublime
  • Edit scss file and then Command+B to compile
  • Edit init.js file to use sass or css files; or if scripts are disabled edit individual import statements in html files


  • debug several variables in one console log console.log({x,y,z});
  • equivalent to doing console.dir(x); but for each x, y, and z
  • Professor Frisby's Mostly Adequate Guide to Function Programming with JavaScript
  • You Don't Know JS - A book series on JavaScript YDKJS by Kyle Simpson
  • Document fragment vs innerHTML vs looped appendChild
  • Callbackhell
  • Trianglify is a Javascript library for creating unique, aesthetically pleasing triangle patterns
  • setTimeout runs once: setTimeout(() => {functionName(param)}, 1000);
    setInterval runs (who would have guessed) at an interval!, and issues an ID, which can can be assigned to a variable and used and also then cleared
    iID = setInterval(() => {functionName(param)}, 1000);


  • Official Node.js website
  • npm init to create package.json
  • npm i XYZ or npm install XYZ to install XYZ into local "node_modules" folder
  • node app.js` or node app` to run app
  • npm rm XYZ to remove XYZ
  • npm uninstall XYZ to remove XYZ
  • process.stdout.write("Downloading " + data.length + " bytes\r"); to print to console in same line as before...
  • process.stdout.write("Downloading " + data.length + " bytes\033[0G"); to print to console in same line as before...


Cross references - see also Pandoc & Git Hooks.

  • Dillinger: For creating, editing and exporting markdown files in html.

Web Development

March 2020

General Guides / Checklists / Roadmaps

Tools / Resources

Service Workers

Intersection Observers


  • No Code: The best way to write secure and reliable applications. Write nothing; deploy nowhere.

Search Engine Optimasation (SEO)





Responsive Web Design


Social / Sharing / Structured Data

Interesting Sites


January 2023


October 2017

  • Texcount fix, Perl, Texcount.Perl - Comment out section on win stuff at top
  • Table Generator: For creating, editing tables in Latex, html, markdown, etc.
  • Latex Equation Editor: For creating, editing Latex equations.


March 2020



  • How to connect: sudo mysql -u USERNAME -p (enter sudo pw first, and then the mysql user pw)

VS Code

October 2017



October 2017


October 2017


  • Reference WordNet Interface for NLTK
  • Import wordnet from nltk.corpus import wordnet as wn
  • Loop through lemmas for l in synset.lemmas():
  • Lemma name as string str(
  • Antonyms synset.lemmas()[0].antonyms()
  • Install NLTK 3.0.0b1 pip install -Iv


  • Reference NLTK Data
  • Install nltk data: (1) python
  • Install nltk data: (2) import nltk
  • Install nltk data: (3)
  • or'stopwords') and'wordnet')
  • add path of nltk_data folder to system path
  • or better yet add it to the local repo and code:
  • import nltk


April 2020

See also info on screen

Install Python 3.8

Random Useful Links

Python WSGI ('Whiskey') Server

Switch the /usr/bin/python link to point to current python link

  • cd /usr/bin
  • rm -f python
  • ln -s /Library/Frameworks/Python.framework/Versions/Current/bin/python python


  • Reference Pip
  • Upgrade pip pip install -U pip
  • Upgrade pip python -m pip install -U pip
  • Install NAME pip install NAME
  • Install NAME version 1.0.4 pip install NAME==1.0.4
  • Install specific version pip install -Iv Link-to-tar.gz
  • Uninstall NAME pip uninstall NAME
  • Show installed packages pip list
  • Show out-of-date packages pip list --outdated
  • Upgrade NAME pip install --upgrade NAME
  • To freeze local requirements pip freeze --local > requirements.txt
  • To freeze requirements pip freeze > requirements.txt
  • To install from requirements pip install -r requirements.txt


  • Python 3 reference Virtualenv
  • Old reference Virtualenv
  • Python 3: create a new venv python -m venv NAME
  • Old: Create a new venv virtualenv NAME
  • Activate NAME . NAME/bin/activate
  • Activate NAME NAME\scripts\activate
  • Deactivate deactivate


  • Reference Virtualenvwrapper-win
  • Reference Virtualenvwrapper
  • Make a new venv mkvirtualenv ENVNAME
  • List all virtualenvs workon or lsvirtualenv
  • Activate a virtualenv workon ENVNAME
  • Deactivate virtualenv deactivate
  • Remove virtualenv deactivate and then rmvirtualenv ENVNAME
  • Bind ENVNAME to project setprojectdir <project_path>
  • Bind ENVNAME to project setvirtualenvproject [virtualenv_path project_path]
Virtualenvwrapper-win not finding
  • Edit C:\PythonXY\scripts\mkvirtualenv.bat with the following changes on lines 47, 48
  • Line 47 virtualenv.exe %*
  • Line 48 REM python.exe "%PYHOME%\Scripts\" %ARGS%


  • C:\Python27;C:\Python27\Scripts;
  • Set folder for virtualenvs export WORKON_HOME=$HOME/.virtualenvs
  • Set folder for virtualenv projects export PROJECT_HOME=$HOME/Desktop/PhD_Offline/Git_code
  • source /Library/Frameworks/Python.framework/Versions/2.7/bin/
  • PATH="/Library/Frameworks/Python.framework/Versions/2.7/bin:${PATH}"
  • PYTHONPATH="/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/:${PYTHONPATH}"
  • export PATH

Surface Pro 4

October 2017

  • Get battery report: powercg /batteryreport


  • Dashboard Login
  • Install the toolbelt
  • Python Flask Guide
  • Login heroku login (needs authenticator code)
  • Git Push git push heroku master only in git bash !
  • make sure reqs.txt is called requirements.txt

Acrobat Reader

October 2017

  • Forum Help
  • Uncheck preference located under Edit > Preferences > Documents "Open Tool pane for each document"


Category Filters:





Yuki & Aya and her kittens :)


cats-004 cats-003 cats-001 cats-002