Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
54 changes: 54 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Deploy to GitHub Pages

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
cache: npm

- name: Configure GitHub Pages
uses: actions/configure-pages@v5

- name: Install dependencies
run: npm ci

- name: Build site
run: npm run build

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: build

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
10 changes: 4 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
node_modules
.svelte-kit
build
_site
.sass-cache
.jekyll-cache
.jekyll-metadata
vendor

Gemfile.lock
.DS_Store
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Generated output
.svelte-kit
build
_site

# Package managers
package-lock.json
11 changes: 11 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}
37 changes: 0 additions & 37 deletions 404.html

This file was deleted.

33 changes: 0 additions & 33 deletions Gemfile

This file was deleted.

58 changes: 45 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
# 5of12 Github Pages site
For blogs and content relating to our projects and open source repositories.
# 5of12 Site

## Setup instructions
This repository builds the 5of12 site as a static SvelteKit app and deploys it to GitHub Pages.

The site is built with Jekyll, setup instructions here: [Jekyll Installation](https://jekyllrb.com/docs/installation/)
That will take you through making sure Homebrew, Ruby and Jekyl are installed.
The previous Jekyll/Ruby build has been removed. GitHub CodeQL default setup was also disabled because it was still trying to analyze Ruby even though the site no longer contains Ruby source.

Once that's all setup install the github pages gem `gem install github-pages`
## Development

### Local testing
```bash
npm install
npm run dev
```

You can build and serve the files locally with Jekyll using `bundle exec jekyll serve` optionally with `--livereload`
The dev server defaults to Vite's local URL, usually `http://localhost:5173/`.

### Remote builds
## Quality checks

Merges to main will trigger a github action to build the site using the same Jekyll config.
```bash
npm run check
npm run build
```

# New Content
`npm run build` writes the static site to `build/`, which is the directory uploaded by the GitHub Pages workflow.

New pages are created as Markdown files that get converted to html following the rules of the Theme.
## Content

New blogs can be added by placing them in the `_posts` directory with a name formatted as `YYYY-MM-DD-title-of-blog`
- Home and about pages live in `src/routes/`.
- Shared site data lives in `src/lib/content/site.ts`.
- Journal posts remain in `_posts/` and are parsed at build time.
- Static images and media are served from `static/assets/`.

## Markdown Rendering

Journal posts are currently parsed from the old Jekyll-style `_posts/` directory by `src/lib/server/posts.ts`. This preserves the existing content during the Svelte transition, but it is not as capable as the previous Jekyll Markdown pipeline.

For future Markdown work, consider adding mdsvex so posts can be rendered as first-class Svelte content with component support, layouts and better authoring ergonomics:

- Svelte CLI mdsvex docs: https://svelte.dev/docs/cli/mdsvex

Likely migration path:

1. Add mdsvex to the Svelte config.
2. Move posts from `_posts/` into a Svelte-friendly content route or content directory.
3. Keep existing front matter fields: `title`, `author`, `date`, `coverImage`.
4. Replace the custom Markdown parsing in `src/lib/server/posts.ts` once mdsvex owns rendering.

## Deployment

GitHub Pages deploys from `.github/workflows/deploy.yml` on pushes to `main`.

The workflow:

1. Installs Node dependencies with `npm ci`.
2. Builds the static SvelteKit site with `npm run build`.
3. Uploads the `build/` directory to GitHub Pages.
54 changes: 0 additions & 54 deletions _config.yml

This file was deleted.

26 changes: 15 additions & 11 deletions _posts/2025-04-07-a-cacophony-of-gestures.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ author: Pete Nancollis
---

# TL;DR
We built a gesture control library for Unity. One that isn’t tied to a particular source of input data and works outside XR. It is modelled on an Anticipation, Action, Reaction cycle to help development run smoothly. It’s called [Cacophony](https://github.com/5of12/cacophony) as a nod to the complexity that arises from the interactions between gestures when building applications with multiple controls.

We built a gesture control library for Unity. One that isn’t tied to a particular source of input data and works outside XR. It is modelled on an Anticipation, Action, Reaction cycle to help development run smoothly. It’s called <a href="https://github.com/5of12/cacophony">Cacophony</a> as a nod to the complexity that arises from the interactions between gestures when building applications with multiple controls.

![Hands in a range of poses. forming confusing gestures](/assets/CacophonyOfGestures.png "Cacophony of gesturing hands")

# Intro
# Intro

Spatial computing applications give us the possibility of using our hands to interact with content without additional controllers. XR headsets such as those from Apple, Meta and Microsoft have built fantastic spatial interaction systems and they provide robust tooling to help developers work with their respective platforms. But what are you to do if you want to apply these techniques outside the XR space?

In the world of flat screens, spatial computing often starts with something that sounds simple. Using hand gestures to trigger behaviour in an application. Despite it being a common entry point to this world, there aren’t a lot of tools available to help you implement it. So we built one!
Expand All @@ -20,45 +22,47 @@ In the world of flat screens, spatial computing often starts with something that
Developing a gesture control application is a journey of discovery. It’s very easy to fall into some common design traps that make building a reliable solution difficult and expensive. This is why we named our library Cacophony, it’s a friendly warning about what’s to come so that you can be prepared!

## What is a gesture?
A gesture can be pretty much anything you do with your hand that a sensor can detect. With optical hand tracking solutions that can detect the positions of your fingers, that means any hand pose you can think of and any kind of motion you might associate with it.

A gesture can be pretty much anything you do with your hand that a sensor can detect. With optical hand tracking solutions that can detect the positions of your fingers, that means any hand pose you can think of and any kind of motion you might associate with it.

![Video of showing a virtual hand grabbing and releasing, then confetti is release](/assets/Grab-Release.gif "Grab Release gesture")

This is a lot of freedom for designing your system. But, like a kid let loose with a new set of paints, there’s suddenly colours everywhere and it’s all merging into brown. Gestures can be thought of as discrete, but the boundaries between them are fluid. To successfully design with gestures we need constraints.

## Turning Gestures Into Action
## Turning Gestures Into Action

Cacophony doesn’t prevent you from defining as many gestures as you like in your application. It does however try to make things easier to manage and control.

Cacophony breaks down gestures into a set of components which combine to form an Anticipation, Action, Reaction cycle.
Cacophony breaks down gestures into a set of components which combine to form an Anticipation, Action, Reaction cycle.

First we detect the shape of the hand and determine if that shape matches a given Gesture Definition. A Gesture Definition is a collection of pose descriptions that allow us to specify what is and isn’t the hand shape we are looking for. This forms the anticipation part of the cycle.

> Given a hand in an OK pose 👌

Now we have detected a gesture is occurring, we are ready for action. You’ve put your thumb up, but what are we going to do with it? Gestures are coupled with Actions, which observe the gesture over time and determine if there is intent behind it.
Now we have detected a gesture is occurring, we are ready for action. You’ve put your thumb up, but what are we going to do with it? Gestures are coupled with Actions, which observe the gesture over time and determine if there is intent behind it.

> When moved down ⬇️

The reaction from the application is how users perceive the effect of their actions. Reactions are important at every stage of a gesture from initiation to successful detection completes. Clear feedback helps people learn the system and gives them confidence that their inputs are being processed as they expect.
The reaction from the application is how users perceive the effect of their actions. Reactions are important at every stage of a gesture from initiation to successful detection completes. Clear feedback helps people learn the system and gives them confidence that their inputs are being processed as they expect.

Cacophony actions provide a series of events for your application to react to, representing each stage of detection. This makes it a simple process to create rich audio, visual and haptic feedback in response to the gestures detection.

> Then turn on the lightbulb 💡

![Animation of a hand pinching and moving down, turning on a light bulb](/assets/Lightbulb.gif "Lightbulb animation")

When designing applications for gestures it’s important to consider all the stages of the process and not just think of it as a static symbol. Using tools that are structured around this process a designer and developer can quickly iterate on designs and ensure their interactions are clearly defined.
When designing applications for gestures it’s important to consider all the stages of the process and not just think of it as a static symbol. Using tools that are structured around this process a designer and developer can quickly iterate on designs and ensure their interactions are clearly defined.

## About that friendly warning…

With all this freedom to create new gestures comes a temptation to use them. Even with a robust detection system though, the reality of gesture systems is that the complexity grows unmanageable very quickly.

Overlap between different gestures is common, and you have to remember them all while avoiding accidentally performing them when you don’t want to. If you attach a sound effect to each gesture then you quickly create a cacophony of intentional and unintentional sound.
Overlap between different gestures is common, and you have to remember them all while avoiding accidentally performing them when you don’t want to. If you attach a sound effect to each gesture then you quickly create a cacophony of intentional and unintentional sound.

![Animation of a hand pinching and moving down, turning on a light bulb](/assets/Cacophonous.gif "Cacophonous animation")

With restraint and careful design however you can build some fun and engaging applications that bring the spatial computing world into the real world.


## Try it yourself
If you’ve read this far you are probably interested in trying the system out for yourself. The good news is you can! Check out the [Cacophony](https://github.com/5of12/cacophony) project on github and take a look at the example projects in our [Cacophony Playground](https://github.com/5of12/Cacophony-Playground) for some inspiration.

If you’ve read this far you are probably interested in trying the system out for yourself. The good news is you can! Check out the <a href="https://github.com/5of12/cacophony">Cacophony</a> project on github and take a look at the example projects in our <a href="https://github.com/5of12/Cacophony-Playground">Cacophony Playground</a> for some inspiration.
4 changes: 2 additions & 2 deletions _posts/2025-09-04-playtonik-looping.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ author: Tom Cartwright

<iframe width="100%" src="https://www.youtube.com/embed/ByBfa3axGeE?si=slml7JWGrF8-ROK1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="border-radius: 15px; aspect-ratio: 1.78"></iframe>

Get Playtonik on the [App Store](https://apple.co/4oTOgyW)
Get Playtonik on the <a href="https://apple.co/4oTOgyW">App Store</a>

---

Expand Down Expand Up @@ -44,7 +44,7 @@ Now we're getting notes out, but they're not as we performed them - which for a

To get around this, I thought of using the playback thread as a lookahead process instead of triggering the notes directly. Each fixed update frame, the thread will run and gather all the notes that should be played in the following frame. These notes are then collected into a buffer which can be read from the main thread, meaning that each frame only plays notes that should be played in that window of time.

<img src="/assets//looper/lookahead_diagram.jpg" style="width: 100%">
<img src="/assets/looper/lookahead_diagram.jpg" style="width: 100%">

Success! The notes being played back now feel much more in time with how they were originally performed and the feature feels much more useful.

Expand Down
Loading