Note
What a PX page actually weighs, and the switch that halves it
Every ux or PX-in-a-browser session loads 718 KB of framework before it draws anything, and a stock station sends all of it uncompressed. Both numbers are readable from the jars, without a station.
- PX graphics
- JACE
- Performance
The payload is declared in Java, so it can be counted
"Can the controller serve this graphic?" is normally answered with an opinion — it depends, keep it simple, use a Supervisor. It has a measurable form: when an operator opens the view, how many files and how many bytes does the station send, and how much of that is your drawing?
That is answerable without a running station, because the framework declares its own
client-side dependency graph in Java. Every module that ships browser code registers
a BJsBuild singleton naming exactly one pre-built bundle plus the builds
it depends on, and the page writer resolves that set into the nested
require() calls it writes into the page. Read the classes and you have
the payload the framework will ask for, per view, exactly as the framework computes
it. The figures below come from a stock Niagara 4.15 install.
The floor: 718 KB before your graphic exists
Every session loads the same bootstrap, whatever the view contains. The requirejs alias table decides which file each name resolves to, and at stock defaults it resolves to the minified copy of each library:
| File | Raw | Gzipped |
|---|---|---|
| require.min.js | 17.2 KB | 6.4 KB |
| js.built.min.js (20 AMD modules) | 50.7 KB | 23.7 KB |
| jquery.min.js | 85.5 KB | 29.7 KB |
| bluebird.min.js | 79.6 KB | 22.8 KB |
| handlebars.min.js | 87.3 KB | 26.6 KB |
| underscore.min.js | 19.2 KB | 7.4 KB |
| hbs.built.min.js | 7.2 KB | 3.2 KB |
| lex, log and css plugins | 8.7 KB | 3.9 KB |
| baja.js (the BajaScript plugin) | 9.0 KB | 2.8 KB |
| bs.built.min.js (202 AMD modules) | 354.0 KB | 89.8 KB |
| Total | 718.4 KB | 216.3 KB |
Twelve files, 718 KB, 222 AMD modules, and not one pixel of anybody's building.
BajaScript is the largest single item and it is not loaded by a
define([...]) list that a scan would find — the baja!
plugin requires it at runtime, which is why it is missing from most estimates of
this.
What the view itself adds
Resolving each build's declared closure gives two honest figures rather than one.
The certain column is the floor plus every bundle and stylesheet in the
resolved graph: those are written into the page's require() blocks and
fetched whether or not a particular widget is used. The ceiling adds every
loose file a bundle member declares that no loaded bundle provides — the Ace code
editor at 446.9 KB, the icon sprite stylesheet at 216.2 KB, moment, the dialogs
plugin, jQuery contextMenu. Those are fetched lazily, when something that needs them
is instantiated, so a real page lands between the two columns.
| View | Certain (raw / gzip) | Ceiling (raw / gzip) |
|---|---|---|
| A bare bajaux widget | 869 / 260 KB | 876 / 262 KB |
| PX served to a browser (hx container) | 1,871 / 507 KB | 2,816 / 707 KB |
| Graphics primitives (gx) | 1,994 / 530 KB | 2,870 / 713 KB |
| PX with the standard widget kit | 2,270 / 597 KB | 3,146 / 780 KB |
| A chart on the page | 2,585 / 682 KB | 3,735 / 956 KB |
| An analytics chart | 3,111 / 793 KB | 5,006 / 1,280 KB |
One bundle dominates all of them: the web editors bundle, 916 KB carrying 417 AMD modules — field editors, property sheet, nav tree, composite editor — pulled in by the graphics module, the standard widget kit, the chart module and the hx container alike. A read-only floorplan on a wall panel gets the whole editing toolkit, because the dependency is declared at build level rather than at view level.
Counted as AMD modules instead of files, a PX page with standard widgets is about 900 modules arriving in 31 requests. That ratio is the bundling doing its job, and it is the reason webdev mode belongs nowhere near a site station: with it on for a module, requirejs fetches that module's members individually and the same view goes from tens of requests to several hundred.
Gzip is off by default, and it is one hidden slot
This is the finding worth acting on. The station's web server does have a gzip
handler, and its MIME list covers JavaScript, CSS, HTML, plain text, JSON and the
XML types — everything in the tables above. But the handler is only added to the
handler collection when the web service's gzipEnabled property is true,
and that property is declared HIDDEN with a default of false.
You will not see it on the property sheet until you show hidden slots.
Nothing downstream compensates. Module resources are served from
/module/* by a file servlet whose only content-encoding path requires
the file on disk to already be a gzip file, which no bundle is. The
heap-memory file cache that does compress is mapped to /vfile/* only.
Out of the box, a PX page ships about 2,270 KB of uncompressed JavaScript and CSS to every operator whose browser cache is cold. With the switch on, about 597 KB. As arithmetic rather than measurement: on a 10 Mbit/s link that is 1.9 s of transfer against 0.5 s; on a 2 Mbit/s site uplink, 9.3 s against 2.4 s, per cold operator.
Two caveats, both checkable. The gzip handler is present in the compact3 build of the web server that ships in the controller's own distribution file, so this is not a Supervisor-only switch. And the trade is controller CPU: this path is not cached, so it compresses per response. Turn it on deliberately, having tested it on your own hardware, before doing it at a site where many operators log in cold at once.
The second load is nearly free, and every upgrade makes it cold
Module files are long-term cacheable by construction: because they live in a
module's space, the servlet sends Cache-Control: private, must-revalidate,
max-age=2592000 — thirty days. The second graphic an operator opens costs
almost nothing, which is the right design.
It has a consequence people meet without recognising it. The page's requirejs config
carries a urlArgs cache-buster derived from the last build time, so
after a station upgrade or a module install every operator's first graphic is a full
cold load again. If that is 2.3 MB uncompressed per operator, the morning after a
Tuesday-night upgrade is when the site link gets measured for you.
Caching also switches off in two places that are easy to confuse with webdev: the
servlet stops sending those headers if niagara.web.addExpires is set to
false, or if its own file-servlet webdev flag is enabled. That is a different switch
from the per-module one that makes requirejs fetch loose files.
The controller has no image pipeline at all
The other half of a graphics budget is the artwork, and here the platform decides
for you. The controller platform's distribution files name their VM as a compact3
ARM build, and that JVM's rt.jar can be counted: it carries
zero classes under java/awt, javax/swing,
javax/imageio and java/beans. The 64-bit JRE shipped in
the same install carries 599, 1,951 and 89 respectively.
So nothing in a station on that platform can decode, scale, crop or recompress an image. There is no server-side thumbnail and no second chance: the render you engineer is the file every browser downloads, at the pixel dimensions and in the format you saved it. On a floorplan job that single fact usually outweighs every JavaScript figure above, because a render exported at 3000 px wide is a multi-megabyte PNG and the controller will not help you.
What to do with these numbers
- Budget the framework, not just the drawing. The drawing is usually the small half.
- Turn the gzip switch on deliberately, tested on the controller, and check it again after an upgrade.
- Size the image once, correctly, in whatever rendered it.
- A chart is not free. Putting one on a landing view adds the chart and history bundles to every operator's first load of that page.
- Reuse view types. Payload is per view type and the browser cache is per file, so five graphics built from the same widget kit pay for the kit once.
- If a view genuinely has to be light — a wall panel, a phone over 4G, a guard's kiosk — the framework floor is the argument for serving a static page from the station's file space and keeping the station as the data source.
For contrast, the three live demos on this site are one self-contained file each, between 9.9 KB and 19.3 KB, one request, no framework floor — which is not a replacement for PX or bajaux, because they are not editable in Workbench and have no component binding. It is the measurement of what the alternative weighs. The artwork side of the same question is in the note on 3D floorplan graphics, what else the controller is carrying is in what actually runs on a JACE, and the sheets themselves are PX graphics work or a bajaux widget when no stock widget will do.
Related
Where this comes up in the work
PX graphics
Niagara PX graphics as a reusable template set: standard HVAC plant sheets, relative-ORD binding, navigation hierarchy and a written graphics standard.
bajaux widgets
Custom Niagara web widgets in bajaux and BajaScript: HTML5 BMS dashboards, HVAC plant views, navigation and charts, bound to live station ORDs and BQL.
More notes
Other things worth writing down
Pure Java, or it will not run on a JACE
A controller is an ARM host with a fraction of a server's memory. Native libraries, JNI and heavyweight dependencies do not survive the move from a PC.
- JACE
- Controllers
- Module development
3D floorplan graphics, and what can actually bind to them
A 3D render is artwork made outside Niagara. What format it arrives in, what can bind to it, and why plan view wins for 200 zone temperatures.
- PX graphics
- JACE
- Workbench
JACE-8000 to JACE-9000: what the licence transfer does and does not do
The JACE 8000 to 9000 licence transfer, step by step: what it requires, what it does not include, and which parts cannot be undone.
- JACE
- Migration
- Licensing
Next step
Tell us the version, the hardware, and what it has to do.
You will get a written scope and a fixed price against it. If the honest answer is that you do not need us, you will get that instead.