/hyper-postprocessing

Add fragment shaders to the Hyper terminal.

Primary LanguageJavaScriptMIT LicenseMIT

hyper-postprocessing

A Hyper plugin that makes it easy to attach fragment shaders to the terminal window.

Inspired by cool-retro-term.

Examples

Cool Retro Term
Some of the effects used in Cool Retro Term ported over by norill. Example
Space travel
A killer space shader made by Kali. Example
Ripple
Ripply effect, using a shader made by bitek. Example
Film
Retro filmy effect, using a shader made by manoloide. Example
Blend
An image blended through only where text is printed. Example
Chalk
A sketchy/chalky shader made by Ruofei Du. Example

Note:

For the latest Hyper version (3.1.0 and above), use hyper-postprocessing v4.

For previous Hyper versions <= 3.0.2, use hyper-postprocessing 3.0.2.

How to setup

In your .hyper.js config file, add hyper-postprocessing to the list of plugins. Then to specify options for this plugin, add a key hyperPostprocessing inside the config entry:

module.exports = {
	config: {
		hyperPostprocessing: {
			// defaults to `${HOME}/.hyper-postprocessing.js`
			entry: 'path-to-entry-file.js'
		}
	},
	plugins: [
		'hyper-postprocessing'
	]
}

Entry file

The entry file will be imported at Hyper startup, and must export a function that returns an object. Every time a new tab or pane is opened, the function will be called and the object will be parsed and passed to postprocessing, which handles all effect rendering. Reading the postprocessing wiki is highly recommended to understand how the EffectComposer works.

The returned object can have the following options:

  • passes (required): array of fragment shader strings (adjacent strings will be incorporated into one EffectPass) or valid instances of a postprocessing Pass that will be used in EffectComposer.

  • fps (default: 60): the frame rate per second

  • coordinateTransform (optional): a function that transforms mouse event coordinates (see note about mouse events below)

  • three+postprocessing (optional): the dependencies to use (see note about peer dependencies below)

Do not include the initial RenderPass that EffectComposer requires. This is done automatically.

Careful of shared instances

Make sure that each pass instance is created every time the exported function is called, so that each tab/pane's effects are self-contained. Otherwise a single pass could be used in multiple contexts, and its properties could become overwritten and inaccurate.

Example

Bad -- one pass will be used for all tabs/panes.

/* path-to-entry-file.js */
const pass = new MyPass();

module.exports = () => {
  return {
    passes: [pass]
  };
}

Good -- no shared passes.

/* path-to-entry-file.js */
module.exports = () => {
  const pass = new MyPass();

  return {
    passes: [pass]
  };
}

Mouse events

If your effects reposition any content inside the terminal, then mouse events will not be in sync with terminal visuals. You can optionally provide a coordinateTransform function to change the coordinates of mouse events.

/* path-to-entry-file.js */
module.exports = () => {
  return {
    passes: [
      `void mainUv(inout vec2 uv) {
        uv.x = 1.0 - uv.x;
      }`
    ],
    coordinateTransform: function(x, y) {
      return [1 - x, y];
    }
  };
}

coordinateTransform will take in the x and y values of the mouse event, and return a tuple containing the modified values for each. The original mouse event will be prevented and stopped, and a new event will be fired at the new location.

The x and y values are similar to uv.x and uv.y used in shaders, in that they both range from 0 to 1 and represent the location of the mouse event by percentage of the terminal screen. So a click at [x=0, y=0] would represent the bottom left corner of the screen and a click at [x=1, y=1] would represent the top right corner. Theoretically you can duplicate any uv transformations made in shaders and use them in this callback to fix any mouse-visual problems.

Quick start

postprocessing already provides a number of effects out of the box (demo). You can use examples/quick-start.js as a starting point to build your own effect, or see one of the example effects for a more custom approach.

Uniforms

  • sampler2D inputBuffer -- the xterm terminal image
  • float aspect -- the aspect ratio of the screen
  • vec2 resolution -- the image width and height in pixels
  • float time -- the amount of time that has passed since the initial render

EffectPasses also gain additional uniforms, courtesy of postprocessing. These will not be available to passes that are not EffectPasses.

  • uniform vec2 texelSize
  • uniform float cameraNear
  • uniform float cameraFar

A note about dependencies

This plugin comes bundled with three and postprocessing as dependencies in order to work upon installation, however those should be viewed more as peer dependencies -- if your entry file makes use of either of them you should install them yourself.

By default this plugin uses postprocessing v6.22.5 and its compatible version of three (v0.132.2), but can use other versions of those if needed. To do this you can add the versions of three/postprocessing to the returned object from the entry file:

/* path-to-entry-file.js */
const three = require('three'); // vX.X.X
const pp = require('postprocessing'); // vY.Y.Y

module.exports = () => {
  return {
    passes: [new pp.EffectPass(null, new pp.VignetteEffect())],
    three: three,
    postprocessing: pp
  };
};