Skip to content
Jeremy B edited this page Apr 20, 2026 · 1 revision

Contributing to NetLogo Web

NetLogo Web and its constituent projects, Galapagos (the web site and model view) and Tortoise (the compiler and simulation engine), are open source software and we welcome contributions.

To get started, sign the Contributor License Agreement.

We accept contributions through the fork-and-pull methodology. Fork the relevant repository and submit pull requests for contributions you'd like to see merged.

Before contributing code, read the Architecture overview and get your local environment Building and Running.

Git Usage and Branching

Development follows standard git best practices: use atomic commits with clear messages and do work on side branches instead of master. Good branch name patterns are wip-feature-name (for "work in progress") and topic-feature-name for larger-scoped work.

Testing

Galapagos

Galapagos includes some automated tests, but at the moment most feature testing for model runs is done manually in the browser. It can be helpful to create special "feature test" NetLogo models that can be quickly loaded and checked to see if the changes are successful.

Tortoise

Tortoise has an extensive test suite. See Tortoise Tests for details on running docking tests, language tests, model dump checks, and the quick-test script to run before finalizing PRs.

Publishing Tortoise Changes to Galapagos

If you have made changes in the Tortoise repository and you want to try them out in the browser using Galapagos, do the following:

  • Launch ./sbt.sh in the Tortoise repository, if you haven't already.
  • Run publishLocalVersioned netLogoWeb.
  • Run publishLocalVersioned compilerJVM.

This will publish the two Tortoise .jars to your local .jar repository (~/.ivy2), making them accessible to your local copy of Galapagos.

  • Find the new version hash that was just published. You can check Git or read it from the logs printed during publishLocalVersioned.
    • Example log entry: [info] Packaging ~/Github/Tortoise/netlogo-web/target/netlogowebjs-1.0-e9dc67a.jar ...
    • So $VERSION_HASH should be e9dc67a.
  • Copy the new version hash into the Galapagos repository's build.sbt, where we set tortoiseVersion.
    • Example: val tortoiseVersion = "1.0-e9dc67a"

If you did not commit your work in the Tortoise repository, the version will be something like 1.0-...$VERSION_HASH-dirty, so include -dirty in your build.sbt version if that is the case.


Code Style

Scala

For Scala code, follow the rules from the NetLogo code formatting wiki page:

Tortoise also has a Scala style checker. Run stylecheck in the sbt console to check all Scala sub-projects.

CoffeeScript

Both Tortoise and Galapagos use CoffeeScript for engine and UI code. Because CoffeeScript uses whitespace to indicate blocks, some formatting is enforced automatically. Both projects also have a CoffeeScript linter that catches common issues. When in doubt, use existing code in the Tortoise engine as a reference.

Formatting

  • Indentation: 2 spaces. No tabs.
  • Trailing whitespace: None.
  • End of file: Always a single blank line.
  • Line length: No hard limit, but prefer wrapping long lines. Use # coffeelint: disable=max_line_length / # coffeelint: enable=max_line_length around sections where wrapping would hurt readability more than it helps.

Naming

  • File names: All lowercase. In Galapagos, use kebab-case (widget-controller.coffee); in Tortoise, all lowercase with no punctuation (abstractagentset.coffee).
  • Variables and properties: camelCase.
  • Constants (module-level, never reassigned): SCREAMING_SNAKE_CASE.
  • Classes: PascalCase.
  • Private members (on class instances): prefix with _, e.g. @_layerDeps.
  • Event names: kebab-case, e.g. 'world-might-change', 'close-button-keydown'.
  • Boolean variables/properties: prefer is/has/can prefixes, e.g. isEditing, hasFocus.

No Implicit Parentheses

Always use parentheses to wrap method calls. Good: console.log(myObj), bad: console.log myObj. The linter enforces this, but it's worth calling out explicitly: the lack of parentheses can make order of operations very confusing, especially since everything in CoffeeScript is an expression.

Type Annotations

All functions should have a type annotation in a comment on the line immediately above the definition. Types use PascalCase.

# (String, Number) -> Boolean
isValidAge = (name, age) ->
  age > 0

Common types:

Notation Meaning
Unit No meaningful return value (void)
String, Number, Boolean Primitives
Array[Type] A JS array of Type
Object[Type] A plain object with values of Type
Any Unknown or mixed type

For complex or composite types, define them with a ### block comment above the code that uses them:

###
  type Config = {
    name:     String
  , maxIters: Number
  }
###

Instance variables declared on a class or in a Ractive data block should be annotated with an inline # Type comment:

data: -> {
  agent:    undefined # Agent
  hasFocus: false     # Boolean
}

Functions

Use -> for regular functions. Use => (fat arrow) only when the function needs to capture the outer this/@, e.g. in callbacks passed to external APIs or setTimeout.

# Good: plain function, no `this` needed
transform = (x) -> x * 2

# Good: fat arrow because @ is needed inside the callback
@_editor.on('focus', => @set('hasFocus', true))

Explicit return is required at the end of Unit functions (functions that produce a side effect and return nothing meaningful). Value-returning functions should omit the explicit return for the final expression.

# Unit function: explicit return
# (String) -> Unit
setTitle = (title) ->
  @set('title', title)
  return

# Value-returning function: no explicit return needed
# (Number, Number) -> Number
add = (a, b) ->
  a + b

For type signatures with function-typed parameters or return values, use => for the arrow within the type annotation (to distinguish it from the surrounding -> structure):

# ((String) => Unit, Array[Widget]) -> Unit
setUpWidgets = (reportError, widgets) ->
  ...

Strings

Prefer double-quoted strings. Use single quotes when the string itself contains double quotes, to avoid escaping.

Use string interpolation ("#{expr}") rather than concatenation.

Objects and Arrays

Use comma-first style for multi-line object literals. Align values when the keys are of similar length and alignment aids readability:

widget = {
  display:  undefined # String
, fontSize: undefined # Number
, source:   undefined # String
}

Single-line objects use spaces inside braces: { key: value }.

The same alignment principle applies to repeated variable assignments on consecutive lines:

@_eventLoopTimeout = -1
@_lastRedraw       = 0
@_lastUpdate       = 0
@drawEveryFrame    = false

Classes and Ractive Components

Use class syntax for plain objects and services (e.g. SessionLite, ViewController). Use Ractive.extend({}) for Ractive components — do not use class extends Ractive.

In Ractive components:

  • Define data: -> { ... } with one property per line, each with an inline type comment.
  • Set twoway: false on edit forms and other components that manage their own submission lifecycle.
  • Put all event handlers in the on: { ... } block.
  • Put reactive computations in the computed: { ... } block.
  • Write templates as triple-quoted heredoc strings (""").
  • Component names in components: are camelCase keys mapping to PascalCase values.
RactiveMyWidget = Ractive.extend({

  data: -> {
    label:    undefined # String
  , isActive: false     # Boolean
  }

  twoway: false

  components: {
    editForm: MyEditForm
  }

  on: {
    'close-button-clicked': ->
      @fire('widget-closed')
      return
  }

  template: """
    <div class="my-widget">{{label}}</div>
  """
})

CoffeeScript Idioms

Prefer if not over unless in most cases. Reserve unless for the rare situation where it genuinely reads more clearly than the if not equivalent. Prefer the existential operator ? over explicit != null or != undefined checks. Use ?. for optional method calls and ?= for conditional assignment.

# Preferred
doSomething() unless config?
result = cache ?= computeExpensiveThing()
viewWindow?.destructor()

Use do -> for IIFEs when you need to create a local closure:

onQMark = do ->
  focusedElement = undefined
  (event) ->
    ...

Comments and Attribution

Write comments in plain English. For non-obvious decisions, include the author's initials and date:

# Kludge to work around strange IE behavior. --Jason B. (4/21/16)

Use # TODO for known shortcomings that aren't being addressed immediately.


CSS

Formatting

  • Indentation: 2 spaces.
  • Declarations: One per line.
  • Braces: Opening brace on the same line as the selector; closing brace on its own line.
  • Blank lines: One blank line between rules.

Value Alignment

Within a rule block, align all values to a common column using trailing spaces on property names. The column is determined by the longest property name in the block:

/* Good */
.my-rule {
  display:         flex;
  flex-direction:  column;
  align-items:     center;
  justify-content: space-between;
}

/* Not preferred */
.my-rule {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

Apply this to all blocks with two or more declarations. Single-declaration blocks need no alignment. Vendor-prefixed properties that are longer than the unprefixed equivalent (e.g. -moz-user-select alongside user-select) are exempt — leave their colons unaligned rather than inflating the column for the rest of the block.

Values

  • Semicolons: Every declaration must end with a semicolon, including the last one in a block.
  • Zero: Use 0 without a unit (margin: 0, not margin: 0px).
  • Redundant shorthand values: Collapse repeated values in multi-value shorthands. padding: 8px 8pxpadding: 8px; padding: 10px 30px 10px 30pxpadding: 10px 30px.
  • Function spacing: Put a space after each comma in CSS functions — rgba(0, 0, 0, 0.5), not rgba(0,0,0,0.5).
  • Shorthand order: Follow standard order for shorthands. For border: width style colorborder: 2px solid black, not border: 2px black solid.

Shorthands

Prefer shorthand properties over a collection of longhand overrides:

/* Good */
.example {
  padding: 6px 6px 6px 0.2em;
}

/* Not preferred */
.example {
  padding:        0.2em;
  padding-top:    6px;
  padding-right:  6px;
  padding-bottom: 6px;
}

Hygiene

  • No empty rule blocks: Remove rules with no declarations — they add noise and no behavior.
  • No duplicate properties: A property listed twice in the same block means one is dead code. Remove it.
  • No redundant values: Remove properties that reset to the browser default unless the intent needs to be made explicit for future readers.

Clone this wiki locally