Here's my five latest blog posts - or you can browse a complete archive of all my posts since 2008.

Running Corel Linux on QEMU

In my latest YouTube video, I talk about Corel Linux, which I think is one of the most interesting “what if?” scenarios in the history of mainstream computing, and show some clips of Corel Linux actually running. It’s not easy getting a 25-year-old Linux distro to boot on a modern PC - but here’s how I did it.

Install QEMU

VirtualBox, HyperV et al won’t work here - Corel Linux is just too old to support most of the virtual hardware they expose to their guest operating systems. Instead, we’re going to use QEMU.

Download it from, install it, get it running. Check it works by running qemu-system-i386 at a terminal window:

> qemu-system-i386 --version
QEMU emulator version 9.0.0 (v9.0.0-12054-g923cf646f4)
Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers

Create a virtual disk

This will create a 2Gb virtual disk file called corel_linux_hd.img:

qemu-img create corel_linux_hd.img 2G

Download Corel Linux

I used the ISO image of Corel Linux 1.2 deluxe from

While you’re there, you might also want to download CorelDRAW and WordPerfect Office

Run the installer

qemu-system-i386 -hda corel_linux_hd.img -cdrom corel_linux_1.2.iso -m 256 -vga cirrus

You should get this:


and then this…


and then this:


Yeah. This is where it gets interesting: the Cirrus VGA driver provided by QEMU displays garbled text until we get into the OS and hack it, but we can’t do that until we’ve got it installed, so we’ll need to walk through the installation without being able to read anything.

You probably also don’t have any mouse support, so use Tab to move forwards, Ctrl-Tab to move backwards, and the Spacebar to click:


Don’t change anything here - “Install Standard Desktop” is already selected so just press Enter:


The next few screens, accept all the defaults - tab to Next> if it’s not already focused, press Enter:



and finally, Install:


If you get to here, you’re on the right track:


Once it’s installed, let it reboot, then at the loading screen select Linux - Text Mode


It’ll happily chunter away for a few screens worth of messages, and then you’ll get a login prompt.

Log in a root with a blank password. (Don’t ask. It was the early 2000s. Things were different then.)

Now we need to configure X11 so that it won’t try to use various accelerated hardware features that don’t work in QEMU.

This bit comes from Ethan Gates’ Corel Linux in QEMU post on – thank you Ethan! – and that post was in turn inspired by Hayden Barnes’ post The one in which I kind of get Corel Linux 1.2 to work 21 years later - thank you Hayden!

Use vim to edit /etc/X11/XF86config. Find the section called “Device”, add three lines:

Option "no_bitblt"
Option "noaccel"
Option "sw_cursor"


Save the file, reboot (shutdown -r now), and select Corel Linux from the boot menu:


Networking support

Networking support in QEMU is powerful, flexible, and incredibly complicated.

Here’s how I made it work. Disclaimer: I don’t 100% understand exactly what all this does… I tried just about every combination I could think of until I found something that worked.

First, you’ll need to install a TAP network driver. This adds another network interface to Windows, which emulates a physical network card. Apparently.

I installed mine using chocolatety:

choco install tapwindows

Then, in Windows network settings:

  1. Rename the new TAP connection to TapWindows
  2. Right-click your main network connection (ethernet, wifi - whatever connects your Windows machine to the internet)
  3. Properties > Sharing > Allow other network users to connect…
  4. Choose your TapWindows connection as the “Home networking connection”

You’ll get a popup about your LAN adapter. The wording here is misleading - it’s not your LAN adapter, it’s whichever adapter you selected as you” Home networking connection”:

If you remember using Internet Connection Sharing with a dial-up modem… this is the same tech, only we’re pretending that QEMU is our house network, TapWindows is the network card connecting our PC to the rest of the house, and “Ethernet” is the dial-up modem that connects to the internet.


Now, run QEMU using this command line:

I’m using Powershell so a backtick ` is a line continuation character.

qemu-system-i386 -hda corel_linux_hd.img `
-m 256 -vga cirrus -audiodev driver=dsound,id=pa1 -device sb16,audiodev=pa1 `
-netdev tap,id=mynet0,ifname=TapWindows `
-device pcnet,netdev=mynet0

That’s telling QEMU “create a network connection using TAP, call it mynet0, and connect it to the TapWindows interface on the host PC”, and then on the next line “then create a virtual device using the pcnet and connect it to the mynet0 network”.

For me, this worked 80% of the time. When it didn’t work, it’s because something meant the Linux guest couldn’t get an IP address from Internet Connection Sharing’s DHCP server. Manually setting the guest IP to, subnet, gateway usually fixed this.

That got me to the point where I could boot Linux, open Netscape, point it at, and browse the world’s first website.


Stuff I Couldn’t Figure Out

I couldn’t get audio working. QEMU audio works fine - I created a Windows 2000 guest just to test this - and I tried both the ac97 and sb16 virtual audio devices in QEMU, but no luck yet.

I also couldn’t figure out how to change the CD-ROM. When you’re running QEMU, Ctrl-Alt-F2 drops you into an emulation console where you can use info block to see which devices/images are connected, and change ide1-cd0 <filename.iso> to change the virtual disk:


But… then I couldn’t work out how to get Corel Linux to mount the disk image, so to install WordPerfect, CorelDRAW!, etc. I had to shut down the VM and then boot it specifying the -cdrom command line switch:

qemu-system-i386 -hda corel_linux_hd.img `
-m 256 -vga cirrus -audiodev driver=dsound,id=pa1 -device ac97,audiodev=pa1 `
-netdev tap,id=mynet0,ifname=TapWindows `
-device pcnet,netdev=mynet0 `
-cdrom .\wordperfect_office_2000_deluxe_cd_1.iso

Ah, the nostalgia.

"Running Corel Linux on QEMU" was posted by Dylan Beattie on 27 May 2024 • permalink

Using the Contour ShuttleXpress with Camtasia

The Contour ShuttleXpress is a controller designed for editing audio and video. I use it all the time in applications like Logic Pro X; the jog dial and shuttle wheel are brilliant for, well, jogging and shuttling around your project’s timeline.


Camtasia is a screen recording and video editing app from the folks at TechSmith, which I use for doing the screen recordings I use in my presentations and workshops.

Out of the box, the ShuttleXpress doesn’t support Camtasia, so I created an application profile for it.

Action Keystroke What it does
Jog Left , Step backwards in timeline
Jog Right . Step forwards in timeline
Shuttle left/right , . Move forwards backwards in timeline. Each position on the shuttle is independently programmable, so “shuttle in left 7” sends , as fast as possible, “shuttle in left 6” sends 60 strokes per second, and so on. It’s not a perfect timeline shuttle but it’s close enough.
Button 1 Ctrl + Home Jump to beginning of timeline
Button 2 Ctrl+Alt+, Move playhead to previous clip
Button 3 Shift+Ctrl+Alt+Left Arrow Extend selection to previous clip
Button 4 Ctrl+Alt+. Move playhead to next clip
Button 5 Shift+Ctrl+S Split all tracks at playhead

The workflow here is:

  1. Shuttle until you find the start of the mistake
  2. Button 5 to split all tracks
  3. Shuttle forward to the end of the mistake
  4. Button 5 again to split all tracks
  5. Button 3 to select the new region with the mistake in it
  6. Delete to remove the region.

and if you switch on “magnet mode” for the track you’re editing, when you delete the mistake it’ll automatically snap the remaining pieces together so it doesn’t leave a gap.

You can download the settings file here:


and import it using Settings Management > Options > Import Settings in the Contour Shuttle Device Configuration app:


Happy shuttling :)

"Using the Contour ShuttleXpress with Camtasia" was posted by Dylan Beattie on 17 March 2024 • permalink

Recording Meetups

For the last few months, I’ve been recording the talks at the London .NET User Group on video so we can post them on YouTube after the event. The videos are online at, and I’ve had some questions about the setup I use to record them. Some from interested people looking to do something similar at their own events, some from people who show up early while I’m still setting up and go “wow, what’s with all the shiny things?” So here’s how it works.


(I made a diagram! Want it as a PDF? Here you go: dylanbeattie-recording-meetups.pdf.)

Short answer:

  • Canon EOS M200 camera
  • Elgato Camlink
  • Elgato Game Capture HD60S+
  • AnkerWork 650 wireless microphones
  • OBS Studio on an M1 Macbook Pro
  • Lots of HDMI and USB cables

For the long answer: the key here is minimal post-production. When it comes to editing two hours of video footage, “tomorrow” turns into “later” turns into “never” with astonishing regularity. Other than editing out any major technical problems, I want to leave the meetup with a video file that’s ready to upload to YouTube.

I also don’t want presenters to have to do any more than plug in HDMI, clip on a microphone, and go. I’ve done a few events where I’ve had to install drivers or sharing software for their “wireless smart display” system: I really don’t like doing that, and I’m not going to ask our presenters to do it either.

In terms of portability, I assume the venue will provide HDMI for the presenter’s slides, and there’s a power socket available to plug everything in. Everything else needs to fit in a backpack and set up in less than fifteen minutes.

The camera I use is a Canon EOS M200. It’s a mirrorless SLR with clean HDMI out: what this means in practice is you can plug it into an Elgato Camlink, plug that into your laptop, and it shows up as a webcam - albeit a really, really good one. The camera runs off a USB power supply so I don’t have to worry about running out of battery halfway through a recording.

Bad news: Canon has apparently discontinued the M200, along with the rest of the M series, in favour of the Canon EOS R series. This makes me sad, because the M200 is a wonderful camera.

Good news: Elgato maintain a list of supported cameras, so any of the cameras on this list will do the trick:

If I can, I’ll bring a proper camera tripod along. If not, I’ll bring a SmallRig “magic arm” clamp and find a convenient chair, lamp, window frame or something to clamp it to.

Slides: Elgato Game Capture HD60S+

To capture the speaker’s laptop, I use an Elgato Game Capture HD60S+ (now superseded by the Game Capture HD60 X) This works like a Camlink - turns HDMI into a USB video signal - but it has an HDMI output that uses passthrough, so I can plug it in between the speaker’s laptop and the venue’s projector/screen and then tap a USB feed off the side into my laptop.

Audio: Ankerwork M650 Wireless Microphones

The first time I tried this, I used a RØDE shotgun microphone attached to the camera, but the audio quality wasn’t great, so I invested in a set of AnkerWork M650 wireless microphones. These are wonderful and amazing and excellent and I can’t say enough good things about them, but I’m a big fan of Anker gear, and folks I know who use the RØDE Wireless ME or the DJI MIC system say the same things about their setup.


Recording: Macbook Pro + OBS Studio

I run all these devices into an M1 Macbook Pro, and then record everything using OBS Studio.

Set OBS to record MKV format, and enable “automatically remux to MP4” in Settings > Advanced.

If you record direct to MP4, a system crash will leave you with an unusable video file, but MKV files aren’t as widely supported, so recording to MKV and then automatically remuxing to MP4 gives you the best of both worlds.

2024-02-20_11-39-52 (1)

This is where the “minimal post-production” bit kicks in. I’ve got four scenes set up in OBS:

Camera Only:

Slides + Camera:


Camera + Slides:


Slides Only:


Plus, before the meetup I’ll make a title slide for each speaker and create a scene with that title slide plus the microphone, which means you can record audio over the title - great for introductions and preamble:


Note that “Slides + Camera” has the slides on the left, and “Camera + Slides” is the same thing, but with the slides on the right. Composition works better if the speaker is facing towards the middle of the frame, so which one of these gets used depends on where the camera is in relation to the speaker on the night.

OK, showtime. Plug everything in, get it working, probably reboot a couple of things because HDMI can be tricksy.

Mic up your presenter, and check the audio in every scene - nothing worse than recording the whole event and finding out later that one of your presents wasn’t recording any sound. Ask your speaker to stand where they’re going to stand, check the camera angle. I clip the other mic on to myself so I can introduce the speaker, repeat questions, etc. and have it come through on the recording.

Then bring up the title slide, hit Record, introduce the talk, and then switch scenes as you go, and all being well, you’ll end up with a MP4 file you can upload straight to YouTube when you get home.

Shopping List:


5 metres seems to be the sweet spot for cable length. Too short, and you’re going to be in the way. Too long and you get weird signal problems, or no signal at all.

  • MicroHDMI > HDMI cable (camera HDMI OUT > Camlink HDMI)
  • Regular HDMI cable (presenter laptop > GameCapture IN)
  • USB-C cable (GameCapture OUT > OBS)

Total investment if you’re starting from scratch will come to just over £1200.

How much?!

OK, you don’t need to spend that all in one go. Get a copy of Reincubate Camo and use your phone instead of buying an SLR camera + Camlink. For slides, you can pick up cheap HDMI splitter and an HDMI > USB capture gizmo on Amazon for about £20 each, and see how you go.

I wouldn’t skimp on microphones, though: as they say in Hollywood, “your ears never blink”, and you can probably get away with significantly worse video quality if the audio is up to scratch.

Here’s two clips I recorded using the old camera-mounted RØDE shotgun mic:

and here’s some recorded with the AnkerWork mics:

So, that’s your lot. Good luck with it, and let me know if you found this useful.

"Recording Meetups" was posted by Dylan Beattie on 20 February 2024 • permalink

Custom Validation Attributes in ASP.NET Core 8

ASP.NET Core 8 rocks. It’s fast, powerful, cross-platform… and, yes, includes jQuery.

Chill. jQuery’s fine. It’s solid, it’s proven, it works, and we’re not here to impress hipsters hiring frontend devs for their Web3 startup, we’re here to build stuff that works (and will probably still work next year.)

Out of the box, ASP.NET Core includes jQuery, jquery.validate and jquery.validation.unobtrusive, which together provide client-side validation that integrates really closely with the server-side validation provided by ASP.NET and System.ComponentModel.

Most of the stuff you’ll find online about how to extend ASP.NET Core validation still works, but there’s a few new things in .NET 8 which make it a little cleaner. Specifically, IDictionary<TKey,TValue> in .NET 8 includes a .TryAdd(key, value) method, which means the MergeAttributes helper method you’ll find in lots of examples of custom validation attributes isn’t required any more.

Here’s how to extend ASP.NET with a custom validation attribute that’ll make a checkbox a required field – the classic “You must accept the terms and conditions” scenario.

The attribute itself:

public class MustBeTrueAttribute : ValidationAttribute, IClientModelValidator {

	public override bool IsValid(object? value) => value is true;

	public void AddValidation(ClientModelValidationContext context) {
		var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
		context.Attributes.TryAdd("data-val", "true");
		context.Attributes.TryAdd("data-val-must-be-true", errorMessage);

You’ll need to add two lines of custom JavaScript to your pages - I add these to the end of _ValidationScriptsPartial.cshtml.

	jQuery.validator.addMethod("must-be-true", (_, element) => element.checked);

Don’t put this code inside the jQuery onload handler (the one that’s normally wrapped in $(function() { }) – it doesn’t rely on any DOM elements and the call to jquery.validator.unobtrusive.adapters.addBool() has to run before the validation code parses your form.

To use this attribute in your view model:

public class SignupPostData {
	[MustBeTrue(ErrorMessage = "You must accept the terms and conditions")]
	public bool AcceptTerms { get; set; }

and then in your Razor view:

@model SignupPostData

<form method="post">
    <label asp-for="@Model.AcceptTerms">
        <input type="checkbox" asp-for="@Model.AcceptTerms"/>
        I accept the terms and conditions
    <span class="form-text text-danger" asp-validation-for="@Model.AcceptTerms"></span>

@section Scripts {
	@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }

or, if you prefer using the HTML Helper syntax:

@model SignupPostData

@using (Html.BeginForm(FormMethod.Post)) {
	@Html.LabelFor(model => model.AgreeToPayment)
	@Html.CheckBoxFor(model => model.AgreeToPayment)
	@Html.ValidationMessageFor(model => model.AgreeToPayment)
	<input type="submit"/>

@section Scripts {
	@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
"Custom Validation Attributes in ASP.NET Core 8" was posted by Dylan Beattie on 24 January 2024 • permalink

Choosing a Chat App For Your Tech Event

WhatsApp, Telegram, Slack, Discord, Signal, Teams… seems like every event you go to these days has at least one “official” chat app, and probably at least half-a-dozen unofficial chat groups set up by attendees and speakers.

If you’re running a conference or seminar, should you set one up? And if so, which one should you use?

Let’s get the inevitable out of the way first: even a small community conference is going to attract a few dozen people, and there’s no way they’re going to agree on which chat app is best. Microsoft Teams is great when all the people in it work for the same company; for guests, even signing in can be a showstopper. For everybody who likes WhatsApp because it’s ubiquitous, there’s somebody who refuses to install it because they won’t use Meta products. For everybody who likes Slack because it’s already installed on their company laptop, there’s somebody who hates Slack because of its convoluted authentication system (hi!). For everybody who loves Discord because it’s where all their friends hang out, there’s somebody who can’t use it on their laptop because corporate IT think it’s only for gamers and crypto.

a woman in clown make-up juggling icons for popular chat systems - WhatsApp, Signal, Discord, Telegram, Slack, Teams

That said, having a chat system for folks participating in your event is an excellent idea. For first-time attendees who don’t know anybody, it’s a great way to connect with people, find out where people are having drinks or meeting for dinner. It’s an easy way for organisers to share updates and last-minute agenda changes.

If you’re running coding workshops as part of your event, setting up a dedicated channel for each workshop works brilliantly for sharing code snippets, URLs and diagrams. When I’m teaching workshops, I always ask the organisers to set up a chat with all the attendees, using something people can run on their laptops – there’s no point sharing 25 lines of JavaScript with somebody if the only place they can see it is a WhatsApp notification on their iPhone.

So, here’s a few guiding principles I’d encourage you to consider if you’re creating a chat for your event.

First, ask yourself: are you supporting a physical event, or creating an online community?

It’s lovely to think that people can use your chat to keep in touch after the event, but they don’t need your event chat to do that. Social media exists. Attendees can spin up their own chats and groups any time, on whatever platform they want to, and keep the conversation going there – somewhere where it isn’t your responsibility to answer questions, provide event support, and potentially enforce your event’s code of conduct. Doing those things well for three days is hard enough; do you really want to do it all year round, for free?

Online communities are inclusive: anybody can join, just send them the link. In-person events are exclusive: you’ve actually got to show up. If you’re not there, you’re not part of it. Sure, there’s a huge amount of overlap, but the kind of chat that happens when twenty people are together in an unfamiliar city trying to find a good sushi place is not the same as the chat that happens when those same twenty people are back home, spread across a dozen timezones, and somebody’s asking if anybody knows a good way to show Git commits in PowerPoint.

Consider creating a workspace just for your event: one of the big advantages of creating a new instance/server/workspace/whatever for each event is that it’s easy to invite the right people, and easy to shut it down when you’re done.

Use channels sparingly

You probably need a general chat, a speaker chat, an announcements channel, and if you have separately ticketed events like workshops or seminars, create a channel for each of those. Don’t create fifty different channels for web, JavaScript, ethics, .NET, IOT, crypto, Java: you’ll overwhelm people with choice, and when they see most of those channels are empty and nobody’s posted anything, they’ll lose interest.

Use familiar tools

Online platforms like Pine and gathertown serve a purpose, but the majority of your participants won’t have used them before, which makes them far less likely to get involved in discussion. Stick to something people already know.

During the COVID lockdowns when many events went entirely online, I saw far more engagement and participation during events that used Slack and Discord than I did in events relying on proprietary platforms - we’re talking thousands of chat messages over a few days, vs fewer than a dozen on some of the dedicated event platforms. Familiarity is important.

Encourage your team to get involved

Folks will use chat to ask questions, ask for help, ask if anybody has an extension cord or a Macbook charger or where the vegetarian food is served at lunchtime. Make sure your event staff are around to give helpful answers.

Don’t rely on it if something’s urgent

One of the things I do before I give a talk is to put my phone in airline mode. You ping me on WhatsApp five minutes before I give a presentation? I’m not going to see that until after I’m done. If you need to talk to somebody urgently, call them – and if that doesn’t work, go and find them.

Shut it down when you’re done

This is the one that I personally find the most frustrating. I’m still in Slack workspaces for events that took place in 2020, and like the Hotel California, I can sign out any time I like but I can never leave. Whenever I set up Slack on a new device, they’re all still listed there. I have Telegram chats and WhatsApp groups and Signal chats for old events… and, once in a while, somebody will try to get hold of me via one of those chats. You know if you archive a WhatsApp chat it doesn’t tell other people you’ve archived it… so they have no idea you’re not seeing their messages?

Go on. Delete your old event chats - and I mean delete. Gone. Forever.

Think how much better you’ll feel.

If it was up to me?

Set up a Slack a few months early. Use it to coordinate programme committee, crew, volunteers. Create an announcements channel, a speaker channel and a hallway track for general chit-chat. A few days before you open the doors, invite everybody who’s going to be there. Keep it open for a week or so after the event. Post an announcement that you’ll be shutting it down, give folks the chance to share their LinkedIn, Twitter, email - and then delete the workspace.

"Choosing a Chat App For Your Tech Event" was posted by Dylan Beattie on 18 January 2024 • permalink