How to use it

Three steps to D47 in the headset.

1 Start SteamVR first, then D47.

Start SteamVR first, then D47, and turn on Draw in the headset.

The panel appears in the headset as an overlay beside the game.

Headset Draw in the headset on SteamVR running D47 draws over Elite as an overlay. It is beside the game, never inside it.

2 Put the panel where you want it.

Say "put the panel down", "lock it to my head", "move it left" or "bring it nearer".

The panel moves; world-locked stays where you put it and head-locked follows you.

put the panel down Ask "lock it to my head" — "move it left" — "bring it nearer" — "tilt it up" World-locked stays where you put it. Head-locked follows you.

3 Set the size and the distance.

Set the distance, size and opacity in the Headset settings.

The panel changes to match, and tilts to face you when it is below eye level.

Headset Distance 1.1 m Size 0.9 m wide Opacity 85% The tilt is worked out from these, so a panel below eye level faces you without being told.

! The one that stops people.

Minimise the desktop window if you want it out of the way.

The overlay stays in the headset; only closing D47 removes it.

The desktop window can be minimised and the overlay stays. They are independent on purpose. Closing D47 is what takes the quad away, not minimising it.
Why it works this way

Directive 47 drawn over Elite in your own cockpit — beside the game, never inside it.

1 It attaches. It never starts anything.

IT ATTACHES looks every few seconds NEVER STARTS SteamVR, on your behalf NEVER HOOKS Elite, or its frame SteamVR first, Directive 47 first, SteamVR restarted halfway through — none of it matters. And it says which of the two it is waiting for: “SteamVR is not running”, or “no headset is switched on”.

2 The panel is the same app, drawn a second time.

THE SAME APP drawn a second time NOT A PICTURE OF IT no screenshot of a window So the windowed version can never do something the headset version cannot.

Point a controller at it, pull the trigger, and it comes with you — position and angle together, so it feels attached rather than dragged. Nothing turns it to face you while you hold it: a panel forced upright and square cannot be tilted to read from below or angled to sit beside you, which is most of what moving one is for.

3 Three levers that all sound like “how big”.

RESOLUTION how much the image can hold more pixels, more rows SIZE how big it looks in the room metres across SCALE how much layout those pixels carry — a density At 75% the panel presents 1365×853 worth of layout and a ship’s slots fit. At 200% it presents 512×320 and you can read it from across the cockpit. More pixels cost more every frame — pick by looking at it, not by picking the biggest number.

Scale and mini are different things, and the difference is the point: scale changes how big everything on the panel is, mini changes how much of it there is. Zooming a panel you cannot read makes it readable; switching to mini gives you less to read.

4 The captions follow the broadcast standard, not a preference.

42 characters the most on one line 3 lines the most for one thing said 20 a second reading speed, which sets how long one stays timed from the end not from when the speech starts Nobody reads along with a voice they can hear. They catch the last line after it has gone — and if you say “stop”, the captions stop with the voice.

The details

Directive 47 in your headset, drawn over Elite in your own cockpit.

It sits alongside the game rather than replacing it. It never hooks Elite, never touches its process, and never takes over the frame — it is an overlay, the same way SteamVR’s own dashboard is.

Ask for it

“is the headset working” “headset overlay off” “mini panel” “full panel”

The order does not matter

SteamVR first, Directive 47 first, SteamVR restarted halfway through the evening — none of that is something you should have to think about. Directive 47 looks for a headset every few seconds and builds everything the moment one appears.

It attaches to SteamVR rather than starting it. If SteamVR is not running, or your headset is switched off, Directive 47 waits and tells you which of the two it is waiting for:

SteamVR is not running. D47 will attach when you start it.
No headset is switched on. D47 will attach when one appears.

Nothing here starts SteamVR on your behalf. Launching it uninvited on a machine whose headset is off takes over your desktop for no reason.

What you get

Two things float in front of you.

The panel — the same panel as on the desktop, not a picture of it. It is the same app drawn a second time, so the windowed version can never do something the headset version cannot.

A Settings row that opens a window is refused, and the panel says so. Some Settings rows open a second window over the desktop panel: what Directive 47 remembers, the directions it has drafted, your notes, the logbook, the audio recorder, the Features list, your own ship cores, macros, switches, and the arrow that clears a stored key. A window cannot appear in a headset at all, so a ray press on one of those says “Not currently supported in VR” over the panel and opens nothing. Everything else on the page is pressed as usual.

Captions — everything Directive 47 says, written underneath. They place themselves and clear themselves, and there is nothing to drag them somewhere you would not see them. You choose which of two places they sit — in your view, or low in the cockpit — and Directive 47 works out the rest.

Moving it about

Say where you want it. The panel moves a step at a time, in whichever direction you name:

“move the panel left” / “move the panel right” / “move the panel up” / “move the panel down” “move the panel closer” / “move the panel further away” “turn the panel left” / “turn the panel right” “tilt the panel up” / “tilt the panel down”

One step is 5 cm, or 5 degrees for a turn or a tilt. Say it again for another step. Turning swings the panel’s face towards that side; tilting up leans it back towards your eyes, which is what a panel sitting below them wants.

It acts on whichever panel is on screen — big or mini — and each keeps its own place. If the panel was still riding your head when you asked, Directive 47 puts it down in front of you first and then moves it, exactly as picking it up with a controller used to.

Or look where you want it and say so. The panel on screen moves to the middle of your view, as far from you as it already was, turned to face you:

“place the panel here” / “put the panel here” / “panel here”

Looking down puts it lower and looking up puts it higher. Directive 47 reads which way the headset is pointing, not your eyes, so it is the direction your head faces that counts. The panel becomes world-locked and stays there; a panel that was riding your head goes at its Distance setting.

No model is needed for any of this. The phrases above are matched by name, so they work with no provider configured.

Or put it back where it started. Forgets where the on-screen panel was placed, sets it world-locked at its default distance, and lets it come to rest in front of you again:

“reset the panel” / “reset the vr panel” / “reset panel position”

Size, curvature, scale and resolution are left alone, and so is the other panel. With no headset session, the anchor and the two settings are reset all the same — the panel comes to rest the next time the headset attaches. Each placement heading on the Settings page carries the same reset as a glyph, for the panel that heading names rather than only the one on screen.

Motion controllers are on by default — see below for what turning them off costs.

Point a controller at the panel, pull the trigger, and it comes with you — position and angle together, so it feels attached rather than dragged. Let go and it stays.

While a ray is on the panel, Directive 47 asks SteamVR for the trigger and grip of that controller at overlay priority, and gives them back the moment the ray leaves. On a default SteamVR install that changes nothing you would notice: taking inputs away from other applications needs SteamVR’s Enable global input from overlays developer setting, which is off unless you turned it on, and without it the game, Virtual Desktop and the dashboard keep the trigger while Directive 47 reads it too. With it on, the hand-back is what returns the trigger — 0.48.6 meant to do that on every path and did not, because SteamVR refused the call it made and said so only in d47’s log; 0.52.2 does, and its log says when the controllers are claimed and given back, and “headset overlay off” then “headset overlay on” still frees a controller on an older build. A controller that stops answering everything some way into a session is a different fault, tracked as #1 and closed as not reproducing on 0.111.3.

Nothing turns it to face you while you hold it. A panel forced upright and square cannot be tilted to read from below or angled to sit beside you, which is most of what moving one is for.

Picking it up makes it world-locked, because picking something up and putting it down is what world-locked means. Directive 47 does not then argue with you about the setting.

If Elite’s own recenter moves your cockpit out from under a panel you had placed, put it back by switching that panel’s lock to head, which brings it to your face, and then nudging it — the first nudge sets a head-locked panel down in front of you, at knee height, before it moves. Two steps where re-anchoring was one; that command was retired in 0.94.0.

Resizing it

Say “resize the panel” and the panel on screen goes into resize mode: a coloured handle runs along each of its edges. Point a controller at an edge or a corner — the handle under the ray thickens — hold the trigger and pull. An edge changes one dimension and a corner changes both, and the opposite edges stay where they were.

“resize the panel” / “panel resize mode” “stop resizing” / “done resizing”

Resizing changes the panel’s shape, and the content reflows into it. Pull the panel wider and it gets more pixels as well as more metres, at the same density, so more room means more rows rather than bigger ones. To make what is drawn larger without moving the edges, zoom instead.

Resize mode is a mode because the panel is something you press all session, and an edge that resized whenever a ray brushed it would spoil presses near the edge. Out of the mode, a press on an edge is a press. In the mode, a press anywhere away from the handles still presses, and the grip leaves the mode rather than going back a page.

A drag stops at a smallest and a largest size, so a panel cannot be shrunk past the point where its handles can be found again. Letting go writes the new size and resolution to the settings of the panel you resized; the big panel and the mini panel each keep their own. A panel that was riding your head is put down in the room when you take hold of a handle, the same as picking it up.

Resizing needs the motion controllers. With them off, the Size and Resolution settings below change the same two things.

A ray can enter and leave without speaking, too. A square glyph beside the help mark opens resize mode on the full panel, when the motion controllers are on. Once the handles are showing, a bar along the bottom offers zoom out, reset, zoom in and done — the same four things the hotkeys and voice already reach, drawn where a ray can put a controller and inset far enough from every edge that pressing one is never mistaken for taking hold of a handle. The mini panel carries the bar too, but no entry glyph: voice and the hotkey are what open resize mode there.

Toggle the headset panel’s resize mode

Ctrl+Alt+S out of the box, and it works anywhere, including while Elite has the foreground — so resize mode is reachable with a controller withdrawn and no model configured. Entering and leaving are silent: the handles appearing, or leaving, is the answer. Asking to enter it with the motion controllers off, or with no headset session, is spoken instead — from inside a headset there is no transcript to read it from.

Each of these four headset keys takes a stick or throttle button as well: press BIND on the row and press the button, and it is bound beside the key rather than in its place. Giving it the same kind twice replaces that one, and CLEAR removes both. The button works anywhere, as the key does.

Zooming it

“zoom the panel in” / “make the panel text bigger” “zoom the panel out” / “make the panel text smaller” “reset the panel zoom”

Each step moves the panel’s Scale one rung along the ladder the desktop window zooms with, and reset puts it back to 100%. It acts on whichever panel is on screen. The panel’s edges do not move; the content is laid out again at the new size.

Zoom the headset panel in

Ctrl+Alt+= out of the box, and it works anywhere, including while Elite has the foreground — the same as the overlay’s own keys. Steps Scale up one rung on whichever panel is on screen. A stick button bound beside the key does the same.

Zoom the headset panel out

Ctrl+Alt+- out of the box, and it works anywhere. Steps Scale down one rung. A stick button bound beside the key does the same.

Reset the headset panel’s zoom

Ctrl+Alt+0 out of the box, and it works anywhere. Puts Scale back to 100%. A stick button bound beside the key does the same.

Settings

Show Directive 47 in the headset

On by default, which costs nothing if you have no headset — it looks, finds nothing, and says so. Turn it off if you use SteamVR for other things and would rather Directive 47 stayed out of it.

“headset overlay on” / “headset overlay off”

Panel content

Full or mini. Mini is the same panel showing less — the gear, the banners and the ask box go, the transcript stays. It is also drawn smaller, so it can sit at the edge of vision.

Mini out of the box. The full panel is 1.1 m across — close to fifty degrees of view — which is a lot to meet before you know it can be moved or shrunk. Switch to full whenever you want it.

Ask for either by name, in whichever words come out:

“big panel” / “large panel” / “full panel” “small panel” / “little panel” / “minimal panel” / “mini panel”

The two keep their own placements, so parking mini out to one side while the full panel stays in front of you works the way you would expect.

Mini means a minimal interface, not a fixed size. It starts at 512 by 280 pixels and 0.34 m across, and both are yours to change — with its own resolution setting, or by resizing it in the headset to whatever shape you want.

Mini carries no controls, only what they were showing you. Its starting size was chosen so the text is readable rather than so there is room — every button on it would be space taken from the thing you opened it for. So a page’s own bar goes: the checklist’s filter, its ordering, its import, its Goals switch. What stays is the list itself, including the switch beside each line, so you can still mark work done from the headset. The big panel keeps the whole bar, and that is the one headset surface where a controller can genuinely press it.

Motion controllers

On by default for new installs. An install that already had a settings file keeps whatever that file held — the default only reaches a fresh one.

What you lose with them off. Nothing on the panel can be pressed in the headset: no buttons, no toggles, no checklist ticks, no steppers or segments, no on-panel keyboard, no scrollbar dragging, no grip-to-go-back, and no Settings tab. You cannot grab the panel and carry it.

What still works. Everything by voice: moving between tabs, going back, scrolling, answering a question the panel is already asking, and placing the panel — see Moving it about above, which is the replacement for the carry.

“motion controllers on” / “motion controllers off”

Placing a surface

Six settings each, and the mini panel has its own copies of all six.

Ask for a change and it lands on the panel you are looking at. The two surfaces keep their own numbers — that is the point of mini, which exists to sit smaller and further out of the way — so “move it closer” means the one currently on screen, and the other one does not move. Both are on this page in full, so setting mini up while the big panel is in front of you is a thing you can do here whenever you like.

“Size” and “Scale” are different questions and it is worth knowing which you want. Size is how big the panel itself is, in metres across — the edges move. Scale is how big the writing and the controls on it are drawn, without the edges moving. Wanting bigger text on a panel that is already the right size is Scale.

Setting What it does
Locking {#panel-lock} Head-locked follows you; world-locked stays where you put it
Distance {#panel-distance} How far in front of you, in metres. Head-locked only — a surface you put down is where you put it
Size {#panel-size} How wide, in metres. Height follows the panel’s proportions, so there is nothing else to set
Curvature {#panel-curve} 0 is flat, 1 is wrapped around you
Scale {#panel-scale} How large the content is drawn, on the same steps the desktop window zooms with
Resolution {#panel-resolution} How many pixels the panel is rendered at, which also sets its shape. The big panel starts at 1024x640, the mini panel at 512x280
Opacity is one knob for both

How solid the panel is, from 0.1 to 1 — and one setting, not one per panel. Everything in the table above is genuinely per-surface: mini exists to be smaller and further out of the way, so its distance, size and drop have to be able to differ. How see-through the glass is does not. It is one preference about how much cockpit shows through Directive 47, and asking for it at half never means half, in one of the two modes.

It used to be two, and that is exactly how it went wrong: “set the opacity to 0.5” landed on the big panel’s copy while the mini panel was the one in front of the Commander, so the number they could see never moved and nothing was broken. A value already set on either panel is carried up to the shared one the first time this version runs, so nobody has to set it again.

Three levers, and they do different things

Size, Scale and Resolution all sound like “how big”, and keeping them apart is worth a minute.

  • Resolution decides how much the image can hold. More pixels, more room for rows.
  • Size decides how big it looks in the room. Metres across, at whatever distance you put it.
  • Scale decides how much layout those pixels carry. It re-measures and rewraps rather than simply magnifying, so it is a density control: at 75% the default panel presents 1365x853 worth of layout and a whole ship’s slots fit; at 200% it presents 512x320 and you can read it from across the cockpit.

Every size on the offered list is the same shape as the big panel’s default, so picking one of them changes how much detail is in the panel and not its proportions. A size a resize produced keeps the shape you pulled it to, and is listed beside them. More pixels cost more to render, every frame, and past the point where the panel covers what your headset can actually resolve they buy you nothing. Pick by looking at it, not by picking the biggest number.

Head-locked is what you want for something you glance at; world-locked for something that lives in a particular corner of the cockpit.

World-locked out of the box, and put down for you the first time Directive 47 runs in your headset: about a metre ahead of wherever you are facing, low enough that the top of the panel sits around knee height, and tilted back so it faces you rather than the ceiling. A panel that follows your gaze is in the way of whatever you turned to look at, which is the one thing a companion beside a flight sim should never be.

That first position is worked out from where your head actually is — the floor comes from your room setup, and the height of the panel from its width and proportions — so it is not a number picked for somebody else’s height or somebody else’s panel size. Move it once and it is yours; Directive 47 never places it again.

Scale and mini are different things, and the difference is the point: scale changes how big everything on the panel is, mini changes how much of it there is. Zooming a panel you cannot read makes it readable; switching to mini gives you less to read.

The mini rows work the same way: locking, distance, size, curvature, scale and resolution — but not opacity, which both panels share. The mini panel defaults to 0.34 m across — it is meant to sit at the edge of vision — against 1.1 m for the full one.

Captions

They follow the broadcast closed-caption standard rather than anyone’s preference:

42 characters   the most on one line
 3 lines        the most for one thing said
 3 lines        the most on screen at once
20 per second   reading speed, which decides how long one stays up
5/6 s .. 7 s    the shortest and longest a caption lingers

A caption stays up timed from when the speech ends, not when it starts. Nobody reads along with a voice they can hear; they catch the last line after it has gone.

A long sentence arrives as several captions, not as its last three lines. Anything that wraps past three lines is shown three at a time, each staying up for the longer of your reading speed and the time the voice takes to say it, until the sentence has been shown in full. The speaking time comes from how fast the voice has recently spoken. If Directive 47 starts saying something new while an earlier sentence is still being shown, the new one takes over: a caption still working through something the voice finished with is no longer captioning.

Sounds that mean something are captioned too. The warning tone that plays a moment before an urgent callout is written in brackets — [interdiction alert], [attack alarm], [heat alarm] — so a Commander reading captions gets the same head start a Commander hearing them does. The loop tones are not captioned, because the panel already shows the loop state; ambience, music and the thinking bed are not, because they are continuous and say nothing.

And a caption says who is talking when it is not Directive 47. Your carrier’s tower and its captain are different people in different voices, and a crew member you address by name answers in theirs — so those lines open with [Tower], [Carrier] or the crew member’s own name. Directive 47’s own lines carry no name: it is the voice the caption band belongs to, and putting a label on every line would be noise. In-game messages from other Commanders are not captioned at all — they are already written out in full, with their sender, in the Transcript’s Journal File reading.

Captions follow what is actually audible rather than what was generated, so if you say “stop” the captions stop with the voice. A caption still sitting there after a silence command is Directive 47 visibly not having stopped.

They stay level with the horizon, not with your head. Head-locked captions follow where you are looking — turn or look down and they come with you — but they ignore how far your head is tilted, so the text runs along the same line the cockpit does instead of along your eyeline. Tilt your head twenty degrees and the captions stay put and level while your view rolls around them. There is no tilt dial and deliberately so: a caption is either level or it is wrong, and an angle you have to trim by hand is a way of shipping it wrong.

If your captions look level but the cockpit does not, that is Elite’s own recenter having been taken with your head tilted — recenter again with your head straight, and both agree.

Caption position

head or world. Two positions, both worked out for you, and nothing else about where captions go is settable — there is no distance, no curvature and nothing to reach out and drag.

head is the default. The band hangs a little below the middle of your view, 1.6 m out, and goes where you go. It is always readable, which is the one thing a caption has to be.

world puts it in the cockpit instead, in a fixed strip between the console and your feet: 0.8 m ahead of where you sit and 0.67 m below your eyeline, so it covers the band from about 37° to 43° down. It stays there while you look around, the way the cockpit does.

Why you might want that: a head-locked caption is the only thing in the headset that does not move when you turn your head. Your eyes report a band holding perfectly still while your inner ear reports a turn, and that disagreement is what motion sickness is made of — so a band that sits in the room instead of on your face is the more comfortable of the two for anyone it bothers. World- locked is also that little bit steadier when you move your head fast: head-locked captions are put level again every time the overlay is served, and the headset carries them rigidly in between, so a quick roll tilts them until the next frame. A strip sitting in the cockpit is not being carried, so there is nothing to catch up.

What it costs, plainly: you have to glance down for it. Head-locked is always in front of you and world-locked is somewhere you look. That trade is yours, which is why this is a row and not a ruling.

The strip is measured from your seated position, not dropped where you happened to be looking, so it cannot drift and there is nothing to put back. If it ever sits wrong, it is your seated position that has moved: use SteamVR’s own Reset Seated Position with your head straight and the band comes with it.

Text is the same size either way. The strip is nearer than the head-locked band, so it is correspondingly narrower — both cover the same 30° of view, and caption size means the same thing in both.

Caption size

small, medium or large. Three sizes rather than a number, because a caption is either legible at a glance or it is not, and there is nothing useful between two adjacent values.

Caption background

How solid the box behind the text is.

A caption sits over a starfield, a station’s floodlights and your own instruments. Text with nothing behind it is unreadable against half of those — which is why broadcast captions have always sat on a box, and why the box is not fully solid, since one you cannot see through is a hole cut in your cockpit. The text stays fully opaque whatever you set; only the box fades.

Reading speed

Characters a second. 20 is the standard’s adult rate, 17 its children’s rate, and 12 is there because reading speed is the one thing about a caption that is a property of the reader rather than of the caption.

Recentre Elite’s view

Say “recentre the headset”, “recenter the headset”, “reset the headset”, “recentre the view” or “reset vr” to press the game’s HMDReset key. It is the recentre_headset action of control_interface, and works in the ship, the SRV and on foot. It recentres Elite’s view only; Directive 47’s own panel keeps its place.

Headset

Not a setting but a state, shown next to the switch — because switched off and SteamVR is not running look identical from the outside unless something says which.

The tool surface, for contributors

get_headset_status

Reports whether D47 is showing in the headset, and if not, why not. Reports only. Takes no arguments.

{"type":"object","properties":{},"required":[],"additionalProperties":false}

show_in_headset

Shows D47 in the headset, or stops showing it — the same row as the switch above, reached by voice. It answers with the status rather than an acknowledgement, because switching it on is not the same as it appearing: with no runtime installed the setting takes and nothing shows.

{"type":"object","properties":{"on":{"type":"boolean","description":"True to show D47 in the headset, false to leave SteamVR alone."}},"required":["on"],"additionalProperties":false}

move_headset_panel

Moves the panel that is on screen one step at a time. A world-locked panel’s position is not in settings.json at all — it is an anchor pose in view state, written until now only by a completed carry — so this is a delta applied to that anchor rather than a settings row. The two placement fields that look like the obvious answer, Drop and Pitch, are read only by the head-locked path, and the default lock is world: rows for them would move nothing you could see.

{"type":"object","properties":{"direction":{"type":"string","description":"Which way. turn-left and turn-right swing the face of the panel towards that side; tilt-up leans it back to face the Commander.","enum":["left","right","up","down","nearer","further","turn-left","turn-right","tilt-up","tilt-down"]},"steps":{"type":"integer","description":"How many steps, 1 to 20. One step is 5 cm or 5 degrees. Defaults to one."}},"required":["direction"],"additionalProperties":false}

Up and down are the room’s vertical, not the panel’s own, and nearer and further run along the floor rather than along the tilted face — otherwise bringing a panel closer would raise it at the same time, which is one gesture doing two things.

place_headset_panel_here

Writes a new anchor for the panel on screen: centred on the headset’s forward line, pitch included, with its face turned back at the head and no roll. The distance is from the head to the existing anchor’s centre for a world-locked panel, and the surface’s Distance otherwise. The head pose is read when the command runs, after transcription.

{"type":"object","properties":{},"required":[],"additionalProperties":false}

restore_headset_panel_position

Puts the panel on screen back where a fresh install puts it: removes its anchor from view state and resets its lock and distance rows to their defaults, world and the surface’s own. It acts even with no headset session — a world-locked panel with no anchor rests itself on the next active tick, the same as one that has never been placed.

{"type":"object","properties":{},"required":[],"additionalProperties":false}

zoom_headset_panel

Steps vr.current.scale one rung along ZoomLadder, or back to its default, so it lands on whichever panel is on screen.

{"type":"object","properties":{"direction":{"type":"string","description":"in draws it one step larger, out one step smaller, reset at 100%.","enum":["in","out","reset"]}},"required":["direction"],"additionalProperties":false}

resize_headset_panel

Turns resize mode on or off. The mode lives in the headset host rather than in settings, so it ends with the session. Turning it on is refused with the motion controllers off, since nothing could then take hold of a handle.

{"type":"object","properties":{"on":{"type":"boolean","description":"True to enter resize mode, false to leave it."}},"required":["on"],"additionalProperties":false}

It exists because asking to see the panel used to reach get_headset_status, that being the only headset-shaped thing on the surface — so “show the VR panel” was answered with “the overlays are dark, Commander”, which is true and is not what was asked. set_setting could always have done it; nothing pointed at that.

An overlay application, never a scene one — that is what keeps D47 clear of anything resembling game injection. Elite renders through OpenVR, which is why this is OpenVR and not OpenXR.

Three states, and the difference between the first two matters. Unavailable is no runtime installed; Connecting is a runtime present but no session; Active is up. VR_Init returns Init_HmdNotFound while vrserver.exe is already running: SteamVR is running and a headset is present are different facts. VR_Init also starts SteamVR when it is not running, so it is called only once a session already exists and a headset is present — checked with the process list and VR_IsHmdPresent, neither of which starts anything. Recovery rebuilds rather than repairs: a session that has gone away leaves every overlay handle stale, and a stale handle fails forever with a plausible error.

Two quads: com.dseelinger.d47.panel and com.dseelinger.d47.captions. Apparent text size is the texture’s pixel count and the quad’s width in metres together, so mini is a genuinely smaller image as well as less content — drawing the full panel and hanging it nearer gives text a third of the size.

Out of the box, before anything is resized:

full       1024 x 640 px   at 1.10 m wide
mini         512 x 280 px   at 0.34 m wide
captions    1600 x 340 px   at 0.90 m head-locked, 0.57 m world-locked

The caption quad is the same texture in both positions. The world-locked strip is nearer, so it is narrower by exactly the ratio of the two distances — which is what makes the size steps mean one thing rather than two.

1.4 m was tried first and read as enormous — close to fifty degrees of view, so the panel filled the middle and the cockpit was behind it rather than around it.

The panel is a second instantiation of the same view definition bound to the same view model, rasterised offscreen into a plain pixel buffer and submitted with SetOverlayRaw. About 0.75 ms a frame measured live against SteamVR, of which the rasterise is 0.30 ms, and only when something changed.

The trigger only. A controller reports its grip and other buttons through the same channel, so treating them all as a press means the grip that grabs the panel also clicks whatever the ray was over. And the pointer is the only controller input an overlay gets: SteamVR takes the controllers for its own laser and hands back mouse events. IVRSystem.GetControllerState returns false for controllers that are connected and tracking, silently, and an IVRInput action manifest was built twice in two separate projects, accepted by SteamVR, and never went live.

The pixels go over as raw bytes rather than as a texture. A shared Direct3D texture was the original route, and it left the overlays invisible in the headset while SteamVR reported them visible — with every call returning success, the copy proven correct by test, and the rasterise proven correct by writing a live session’s first frame to data/vr-PanelFull.png and looking at it. SetOverlayRaw hands OpenVR a buffer and lets it do the upload, which removes the graphics device, the adapter match and the share flag along with the fault. Two things it has to get right, both of which come out as a wrong picture rather than an error: the buffer is RGBA where Avalonia rasterises BGRA, and its rows carry no padding, because SetOverlayRaw derives the stride from the width.

Caption line breaks go after punctuation, or before a conjunction or preposition, bottom-heavy where nothing else decides it; when those compete the syntactic break wins. Captions are never curved: two short lines in the middle of the view have no far edges to bring closer.