Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>Binary File DesignerNew to Visual Studio Code? Get it now.
Binary File Designer

Binary File Designer

Devontae Reid

|
33 installs
| (0) | Free
Design binary structures in a form editor — saved as the exact JSON the Binary File Reader loads — and generate a sample binary, a C header, and a layout document for embedded development.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Binary File Designer

Design binary structures in VS Code: a form / tree editor for a *.design.json file that produces artifacts from the design — a sample .bin, a C header, and a layout document — for firmware, EEPROM / flash images, device-config blobs, wire protocols and other fixed-layout binary formats.

It is the design-time counterpart to a binary viewer: instead of decoding an existing file against a format, you lay out the structure and the extension generates from it. Built on the VS Code Custom Editor API; the layout engine, validator and generators are a pure, unit-tested core with no vscode dependency, and nothing in a design is ever executed.

Tabs

The editor has three tabs — Form, JSON, Binary — and they are three views of the exact same document, not three copies of it. There is one *.design.json; whichever tab you edit, the other two update immediately:

  • Change a type or drag a field in the Form tab → the JSON tab's text updates and the Binary tab re-emits the hex dump.
  • Edit raw JSON in the JSON tab → the Form tree rebuilds to match and the Binary tab re-emits.
  • Add or reorder a field by dragging in the Binary tab → both the Form tree and the JSON text update.

Nothing needs a manual refresh or a re-open, and switching tabs never loses unsaved changes — Save / Save draft write the one underlying document, whichever tab you're looking at. Pick which tab a design opens on with binaryDesigner.defaultView.

Form tab

The tree editor. This is where you build the design without hand-writing JSON:

  • Add a field: + Field / + Array / + Enum / + Struct, at the top level or inside any struct.
  • Set its type from the dropdown — every scalar/composite plus each reusable struct you've defined, so nothing needs typing. The size field next to it is only enabled for types that actually need one (bytes, padding, ascii, utf8, utf16) — grayed out otherwise, since a fixed type like uint32 or a struct name has no "size" to set.
  • Make it an array: click [] for a fixed size (it writes uint16[4]), or pick the array type for an items element with a count or a length-prefixed countField.
  • {} adds an inline enum (value → name) table; b adds a bit-range table (name + bits such as 0 or 1-3); rsv marks the field reserved for future use; ⚙ expands a field's details — offset, offsetField, id, display / scale / bias / unit, timestamp, cstring, array view / until, structure size / sizeAlign.
  • Reorder by dragging a row's ⠿ grip; ⧉ duplicates a row (and its subtree); ✕ deletes it.
  • Reusable structures: define a layout once under + structure, then pick it by name from any field's type dropdown, anywhere in the design.
  • Format settings at the top — name, description, version, author, endianness, fileExtensions, baseAddress and one magic signature — are the same keys the Binary File Reader reads to auto-detect and label a file.
  • Undo / Redo (toolbar, or Ctrl/Cmd+Z) step back and forward through every edit — form, JSON, or Binary-tab — the same way any text edit does, because every change is a real edit on the underlying *.design.json.
  • Watch the layout preview (right-hand panel) as you go — struct sizes, offsets, and any gap before an explicit offset update on every change. Offsets print in hex by default (0x10); set binaryDesigner.layoutPreview.offsetBase to decimal for plain numbers instead — applies to already-open editors.

The form editor with the live layout preview

JSON tab

The whole design as text — full control, including shapes the tree can't show (multi-dimensional arrays like int16[4][3]). It's auto-selected the moment a design needs that; otherwise switch to it any time for bulk edits, copy-paste, or diffing. Parse errors and validation issues appear in the status bar above both tabs; fix them and the Form and Binary tabs pick the change up as soon as the JSON parses.

Binary tab

The sample binary this design would emit, as a classic Offset · Hex · ASCII dump with each top-level field's bytes colour-coded. A column legend — the Offset label, the 00 01 … 0F byte-column header, and ASCII — stays pinned to the top of the dump as you scroll, the way a hex editor's does:

  • Hover a byte, a field chip, or a layout-preview row — the other two cross-highlight so you can see exactly which bytes are which field.
  • Add a field without leaving this tab: click a type in the Add palette (u8 … f64, char[16], bytes[4], enum, array, struct, reserve u32, reserve[16], and every reusable struct) to append it, or drag the type onto a byte to insert it exactly there.
  • Reorder: drag a field's chip or its highlighted bytes onto another field; drop on the dashed end zone to move it last.
  • Reserved slots (reserve u32 / reserve[16], or the rsv toggle in the Form tab) render hatched, so future-use space is obviously not "real" data.

Everything here edits the design the same way the Form tab does — it's the same document, just a different way of seeing and rearranging it.

The Binary tab: colour-coded hex dump with the Add palette and a reserved slot

Generators

Three commands, each with a do not edit / regenerate from <source> banner:

  • Generate Sample Binary walks the design and writes <name>.bin from each field's value (or a sensible default), honouring offset and per-field endianness. It reports the byte count and any defaulted fields and offers a round-trip check.
  • Generate C Header emits <name>.h with #pragma pack(1), typedef'd structs / enums / bitfields in dependency order, and a _Static_assert on sizeof so layout drift fails the compile.
  • Generate Layout Doc writes <name>_layout.md — the offset table on its own.
/* Generated by Binary File Designer — do not edit. Regenerate from SensorFrame.design.json. */
#ifndef SENSOR_FRAME_H
#define SENSOR_FRAME_H

#include <stdint.h>

#pragma pack(push, 1)          /* from design.packing */

typedef struct Vec3 {
    float x;  /* acceleration X (m/s^2) */
    float y;  /* acceleration Y (m/s^2) */
    float z;  /* acceleration Z (m/s^2) */
} Vec3;

typedef enum SensorFrame_state {
    SENSOR_FRAME_STATE_IDLE   = 0,
    SENSOR_FRAME_STATE_ACTIVE = 1,
    SENSOR_FRAME_STATE_FAULT  = 2
} SensorFrame_state;

typedef struct SensorFrame_flags {
    uint8_t enabled  : 1;  /* acquisition running */
    uint8_t mode     : 3;  /* 0=raw 1=filtered 2=fused */
    uint8_t reserved : 4;  /* must be zero */
} SensorFrame_flags;

typedef struct SensorFrame {
    uint32_t          magic;          /* file magic 'FRM1' */
    uint16_t          version;        /* format version */
    uint8_t           state;          /* current acquisition state */
    SensorFrame_flags flags;          /* packed status bits */
    uint16_t          reserved0;      /* reserved header word — do not use */
    uint16_t          sample_count;   /* logical number of valid samples in the bank */
    int16_t           temps[4];       /* board temperatures, 0.1 C units, one per quadrant */
    char              label[16];      /* human-readable frame label */
    uint8_t           reserved1[12];  /* reserved for future header fields */
    Vec3              samples[64];    /* fixed sample bank; cycles the three basis vectors */
} SensorFrame;

#pragma pack(pop)

_Static_assert(sizeof(SensorFrame) == 816, "SensorFrame layout drift");

#endif /* SENSOR_FRAME_H */

What you get

  • Form / JSON / Binary tabs — three views of one document, always in sync: a tree editor, a raw-JSON tab, and a live hex dump of the sample binary. Edit any one and the other two update immediately — see Tabs.
  • Tree editor — fields, arrays, enums, bit ranges and reusable structures; a type dropdown listing every reader type and reusable structure (no typing), a [] toggle for a fixed-size array; inline enum (value → name) and bit-range (name : bits) tables; a ⚙ details panel; drag a row's ⠿ grip to reorder; duplicate, nest.
  • Struct sizes — a table of every reusable and inline struct with its own size and alignment, then the frame total, so nested layouts are sized at a glance rather than only the whole frame.
  • Layout preview — Offset · Name · C type · Size (with elem × count for arrays) under the format's endianness, plus the total size and every gap left by an explicit offset.
  • Binary tab — the sample binary as an Offset · Hex · ASCII dump, each field's bytes colour-coded; a column legend (Offset, 00 01 … 0F, ASCII) stays pinned to the top as you scroll; hover a byte, a field chip or a layout row to cross-highlight the other two.
  • Add fields from the hex view — an Add palette (u8 … f64, char[16], bytes[4], enum, array, struct, reserve u32, reserve[16], every reusable struct); click to append or drag onto a byte to insert there.
  • Drag to rearrange — drag a field's chip or its highlighted bytes onto another field to reorder the top-level struct; the .design.json, the hex and the generated header all follow.
  • Reserved slots — mark a field "reserved": true (the palette entries or the rsv toggle) to hold space for the future: it keeps its size and offset, is zero-filled, is left out of the "defaulted" report, renders hatched, and is labelled reserved in the header and layout doc.
  • Types — every type the Binary File Reader understands: uint8 … int64, float32 / float64, bool, char, bytes / hex / binary / padding, ascii / utf8 / utf16 / cstring, enum, flags, timestamp, inline struct, reusable structure names, array; shorthand <base>[<n>] and multi-dimensional int16[4][3].
  • Reusable structures — define a layout once under structures, then use its name as a field or array-element type ("Vec3", "Vec3[64]"); emitted in dependency order in the header.
  • Length-prefixed arrays — "countField": "n" sizes an array from an earlier integer field's value instead of a fixed count.
  • Constants — a named-constant map (numbers, or "A + B" sums) usable as a field value ("value": "MAGIC") or an array countField.
  • Sample values — a JSON literal, a constant name, or a tiny non-Turing-complete expression: ["const", v], ["ramp", start, step?], ["repeat", …], ["random", seed]; struct arrays take struct literals.
  • Identifier validation — every name that becomes a C identifier is checked live and again, hard, before generation: ^[A-Za-z_]\w*$, no C / C++ keyword or stdint typedef, no leading _Upper or __; a one-click fix name snake-cases and de-dupes. validateIdentifier(name, kind) is a pure, unit-tested module.
  • Round-trip check — re-parse the generated bytes against the design and assert every field reads back exactly what was written.
  • Undo / Redo — toolbar buttons (and Ctrl/Cmd+Z, where focus allows) walk back and forward through every edit, from any of the three tabs, since each one is a real edit on the document.
  • Generate on save (opt-in, binaryDesigner.generateOnSave) — re-run chosen generators automatically every time you save a design.
  • Pick your starting tab (binaryDesigner.defaultView), the Binary tab's bytes per row / max bytes shown (binaryDesigner.binaryTab.*), and the layout preview's offset base — hex (default) or decimal (binaryDesigner.layoutPreview.offsetBase). All three apply live, no reopen needed — see Settings.
  • Designs view — an Activity Bar list of every *.design.json in the workspace (size / error count), kept live by a file watcher. Empty (no designs, or no folder open at all) shows Create Design and Open Folder right there instead of a blank panel.
  • Pure, tested core — src/core/ (layout engine, validateDesign, validateIdentifier, the emitters, the parser) has no vscode dependency and is covered by 74 Mocha tests, including golden C headers verified to compile under -Wall -Werror -Wextra -pedantic.
  • Native VS Code look — theme variables throughout, so light, dark and high-contrast all work.

Design-time only — nothing in a .design.json is ever executed; the sample-value expression vocabulary is a fixed enum evaluated by hand-written code, and the header generator only emits, it never imports .h.

Getting started

  1. Run Binary Designer: Create Design (Command Palette) to scaffold a new *.design.json, or open an existing one — it opens in the form editor, not a text editor.
  2. Lay out the top-level struct in the Form tab; watch the layout preview for offsets, size and padding.
  3. Switch to the Binary tab to see the bytes, add fields from the Add palette, and drag fields to reorder — or drop into the JSON tab for bulk edits. All three tabs always show the same design; see Tabs.
  4. Run Binary Designer: Generate C Header (and Generate Sample Binary / Generate Layout Doc) from the palette, the editor toolbar, the Designs view, or the Explorer right-click menu — or turn on binaryDesigner.generateOnSave to run them automatically every time you save.

The design schema — it is a Binary File Reader format

A design is a plain JSON object saved as Something.design.json, and it is exactly the JSON the Binary File Reader loads as a binary-format definition: drop it in the Reader's format folder (or import it) and the same file describes the layout and generates the sample binary and C header. It is validated with the Reader's own validator (vendored under src/core/reader/, refreshed with node scripts/sync-reader-core.js), so "valid here" means "the Reader accepts it". Nothing in it is executed.

{
  "name": "SensorFrame",       // required; becomes the C struct tag and the file names
  "endianness": "little",      // "little" | "big"; default "little"
  "description": "…",
  "fileExtensions": [".frm"],  // optional — the Reader applies the format to these files
  "magic": { "offset": 0, "bytes": "31 4D 52 46" },   // optional signature
  "constants": {               // numbers, or "A + B" sums; usable as a "value" or countField
    "SENSOR_MAGIC": 1179798833,
    "SENSOR_VERSION": 2
  },
  "structures": {              // reusable nested structures
    "Vec3": { "fields": [
      { "name": "x", "type": "float32" },
      { "name": "y", "type": "float32" },
      { "name": "z", "type": "float32" }
    ] }
  },
  "fields": [
    { "name": "magic",   "type": "uint32", "value": "SENSOR_MAGIC" },
    { "name": "version", "type": "uint16", "value": "SENSOR_VERSION" },
    {
      "name": "state", "type": "uint8", "value": 1,
      "enum": { "0": "idle", "1": "active", "2": "fault" }
    },
    {
      "name": "flags", "type": "uint8",
      "value": { "enabled": 1, "mode": 2 },
      "fields": [
        { "name": "enabled",  "bits": "0" },
        { "name": "mode",     "bits": "1-3", "enum": { "0": "raw", "1": "filtered" } },
        { "name": "reserved", "bits": "4-7" }
      ]
    },
    { "name": "reserved0", "type": "uint16", "reserved": true },
    { "name": "temps", "type": "int16[4]", "value": ["ramp", -20, 10] },
    { "name": "label", "type": "char", "length": 16, "value": "sensor-01" },
    {
      "name": "samples", "type": "Vec3[64]",
      "value": ["repeat", { "x": 1, "y": 0, "z": 0 }, { "x": 0, "y": 1, "z": 0 }]
    },
    { "name": "n", "type": "uint8", "value": 3 },
    { "name": "tail", "type": "array", "countField": "n", "items": { "name": "v", "type": "uint16" } }
  ]
}

Everything above is the Reader's own vocabulary — structures, flags / bit ranges as "bits": "1-3", array + items with count / countField, offset, size / length, sections, display / scale / unit, and so on (see the Reader's FORMAT_DEFINITIONS guide). Two keys are authoring-only and ignored by the Reader: value (the sample the generated binary gets) and reserved (a held-for-future slot, zero-filled and annotated in the header).

Field names may be human-readable ("Protocol Version"); the generated C header uses a sanitised identifier (Protocol_Version) and the layout preview keeps the original.

Layout is offset-driven (no packing option)

The Reader places a field at its offset or, when omitted, right after the previous one — there is no compiler alignment. The designer lays out the sample binary the same way and emits #pragma pack(push, 1), so the bytes it writes are the bytes the Reader parses. Add an offset (or a padding / reserved field) wherever you want a gap or alignment; the _Static_assert still checks the real computed size. A structure's explicit size / sizeAlign pads its tail.

Not everything the Reader can do has a fixed layout: a cstring reserves size (default 16) bytes in the sample, an offsetField pointer is packed in place, and a countField that names something other than an earlier sibling or a constant produces 0 elements — each is reported as a warning.

Upgrading a pre-0.6 design

Files written by 0.5.x used a C-struct schema (packing, structs, "array": { "count": 4 }, "bits": [{ "width": 3 }]). They still open and generate — they're upgraded in memory — and the editor shows an Upgrade file button that rewrites them in the Reader format (packing is dropped; hex-string constants become numbers).

Creating a frame — walkthrough

Build the SensorFrame example from scratch.

1 — New design

Run Binary Designer: Create Design from the Command Palette and give it a name that is a valid C identifier (it becomes the top-level struct tag). The file opens in the form editor, not a text editor.

Command Palette: Create Design, then name it SensorFrame

2 — Lay out the header

In the Form tab, use + Field to add the fixed header — magic (uint32), version (uint16), state (uint8), flags (uint8) — pick each row's type from the dropdown and set its design-time value. Toggle {} on a row for an inline enum table and b for a bit-range (name : bits) table. The layout preview on the right updates every keystroke.

The form editor with the header fields and the live layout preview

3 — Add a reusable struct, then use it

In Reusable structures, click + structure, name it Vec3, and add x / y / z as float32. Back in Fields, add a samples field, pick Vec3 from the type dropdown — every structure you define shows up there automatically — and click [] to make it a fixed-size array, then set [n] to 64. No typing required. The Struct sizes panel now lists Vec3 at 12 bytes alongside the growing frame total.

A reusable Vec3 struct, the type dropdown, and the Struct sizes panel

4 — See the bytes and rearrange

Switch to the Binary tab. Each field's bytes are colour-coded; drag a field chip (or its highlighted bytes) onto another to reorder the struct, drag a type from the Add palette onto a byte to insert it there, and use reserve u32 / reserve[16] for space held for the future. Everything writes straight back to the .design.json.

The Binary tab: colour-coded hex dump, the Add palette, and a reserved slot

5 — Generate

Run Binary Designer: Generate C Header (and Generate Sample Binary / Generate Layout Doc) from the palette, the editor toolbar, the Designs view, or the Explorer right-click menu. You get <name>.h + <name>_layout.md (and <name>.bin) next to the design, or in binaryDesigner.outputFolder.

Generate C Header, the confirmation toast, and the new files in the Explorer

Commands

Command What it does
Binary Designer: Create Design scaffold a new *.design.json and open it in the form editor
Binary Designer: Generate Sample Binary walk the design → <name>.bin; report bytes + defaulted fields; offer a round-trip check
Binary Designer: Generate C Header <name>.h (+ <name>_layout.md) — #pragma pack, typedef'd structs / enums / bitfields, _Static_assert on sizeof
Binary Designer: Generate Layout Doc just <name>_layout.md — the offset table
Binary Designer: Round-trip Check emit → re-parse → assert every field reads back what was written
Binary Designer: Open Design open a *.design.json in the form editor

Generators also appear on the editor toolbar, the Designs-view context menu, and the Explorer right-click menu for a *.design.json.

Settings

Open Settings (Ctrl/Cmd+,) and search "Binary Designer" to get a form for all of these, or set them directly in .vscode/settings.json — either works, and either can be set per-workspace or for every workspace (User settings). None of them require a reload; a design already open picks most of them up the next time you open it, and the Binary-tab display settings apply live to editors that are already open.

examples/.vscode/settings.json is a ready-to-copy example covering every setting below, with comments explaining each choice — it's also what's active when you press F5 from this repo, since that opens examples/ as the workspace.

Output

Setting Default Description
binaryDesigner.outputFolder "" Folder for generated artifacts, relative to the workspace root. Empty = next to the design file.
binaryDesigner.generateOnSave [] Run these generators automatically on every save: any of binary, header, layoutDoc. Empty = off. Behaves exactly like running the command by hand.

Layout

Setting Default Description

C header

Setting Default Description
binaryDesigner.header.staticAssert true Emit _Static_assert(sizeof(...) == N) so layout drift fails the compile.
binaryDesigner.header.includeStyle angle #include <stdint.h> vs "stdint.h".
binaryDesigner.header.enumTypedefForFields false Declare enum fields with the generated enum typedef instead of their integer type.
binaryDesigner.header.arrayMax 0 Fixed [MAX] capacity for array.countField members in the header (0 = prompt when generating).

Editor

Setting Default Description
binaryDesigner.defaultView form Which tab a design opens on: form, json, or binary. All three always show the same document.
binaryDesigner.binaryTab.bytesPerRow 16 Bytes per row in the Binary tab's hex dump: 8, 16, or 32.
binaryDesigner.binaryTab.maxBytesShown 8192 Cap on bytes rendered inline; a bigger sample binary shows its size and a prompt to use Generate Sample Binary instead. Raise it for a large frame if your machine can take the extra rendering.
binaryDesigner.layoutPreview.offsetBase hex How the layout preview's Offset column is printed: hex (0x10) or decimal (16). Doesn't affect the generated <name>_layout.md, which always uses decimal.
binaryDesigner.identifierAutoFix true Offer one-click fix name actions for invalid C identifiers in the editor.

Building from source

npm install
npm run compile       # esbuild bundle -> dist/extension.js, then tsc type-check
npm run watch         # incremental esbuild
npm run check-types   # tsc -p tsconfig.json && tsc -p tsconfig.webview.json
npm run lint          # eslint src
npm test              # 58 Mocha unit tests: layout, validator, emitter <-> parser, header golden
npm run example       # regenerate examples/generated/* from examples/SensorFrame.design.json
npm run package       # -> binary-file-designer-<version>.vsix (via @vscode/vsce)

Press F5 for an Extension Development Host with examples/ open. The extension bundles to a single dist/extension.js; src/core/ is pure and type-checked separately from the CSP-locked webview scripts in media/.

Versions

Version Highlights
0.6.0 A design file is now exactly a Binary File Reader format (validated by the Reader's own validator); form edits the Reader schema with a details panel; packing removed; pre-0.6 files upgrade in place
0.5.0 Binary tab hex-dump column legend (pinned Offset \| 00..0F \| ASCII header); layoutPreview.offsetBase (hex/decimal) setting
0.4.0 Size field disables itself for fixed-size types; "packing": false (natural alignment, no #pragma pack); Undo / Redo toolbar; Designs-view empty state (Create Design / Open Folder)
0.3.0 Type dropdown + [] array toggle (no typing); fixed JSON-edits-not-reaching-Form-tab; defaultView / generateOnSave / binaryTab.* settings; README "Tabs" + regrouped Settings docs
0.2.0 Binary tab (colour-coded hex dump, Add palette, drag-to-reorder); reserved fields; per-struct sizes in the preview / layout doc; frame-creation walkthrough
0.1.0 Form / JSON editor, three generators, round-trip check, pure tested src/core/

See CHANGELOG.md for details.

Author

Devontae Reid — www.devontaereid.com · github.com/y0dev/binary_designer_extension

License

MIT — see LICENSE.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft